Repository: PyCQA/bandit
Pull request #851 (2022-03-07 02:06:37)

Add end_col_offset if available
Python 3.8 and above include an end_col_offset attribute on nodes
to indicate the end column offset of the node. If available,
Bandit should include end_col_offset for more clarity on the column
range where the issue was found.

Signed-off-by: Eric Brown <browne@vmware.com>


Patch:
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
index 41273ca..ac80841 100644
--- a/.github/workflows/dependency-review.yml
+++ b/.github/workflows/dependency-review.yml
@@ -11,4 +11,4 @@ jobs:
       - name: Checkout repository
         uses: actions/checkout@v3
       - name: 'Dependency Review'
-        uses: actions/dependency-review-action@v1
+        uses: actions/dependency-review-action@v2
diff --git a/bandit/blacklists/calls.py b/bandit/blacklists/calls.py
index c81e1cd..079da90 100644
--- a/bandit/blacklists/calls.py
+++ b/bandit/blacklists/calls.py
@@ -32,6 +32,9 @@ deserialize untrusted data, possible security issue.
 |      |                     | - dill.Unpickler                   |           |
 |      |                     | - shelve.open                      |           |
 |      |                     | - shelve.DbfilenameShelf           |           |
+|      |                     | - jsonpickle.decode                |           |
+|      |                     | - jsonpickle.unpickler.decode      |           |
+|      |                     | - jsonpickle.unpickler.Unpickler   |           |
 +------+---------------------+------------------------------------+-----------+
 
 B302: marshal
@@ -352,6 +355,9 @@ def gen_blacklist():
                 "dill.Unpickler",
                 "shelve.open",
                 "shelve.DbfilenameShelf",
+                "jsonpickle.decode",
+                "jsonpickle.unpickler.decode",
+                "jsonpickle.unpickler.Unpickler",
             ],
             "Pickle and modules that wrap it can be unsafe when used to "
             "deserialize untrusted data, possible security issue.",
diff --git a/bandit/cli/main.py b/bandit/cli/main.py
index d5eb57d..4758885 100644
--- a/bandit/cli/main.py
+++ b/bandit/cli/main.py
@@ -512,7 +512,7 @@ def main():
         args.context_lines = _log_option_source(
             parser.get_default("context_lines"),
             args.context_lines,
-            ini_options.get("number"),
+            int(ini_options.get("number") or 0) or None,
             "max code lines output for issue",
         )
 
diff --git a/bandit/core/issue.py b/bandit/core/issue.py
index 20e4331..553cda6 100644
--- a/bandit/core/issue.py
+++ b/bandit/core/issue.py
@@ -85,6 +85,7 @@ class Issue:
         lineno=None,
         test_id="",
         col_offset=0,
+        end_col_offset=0,
     ):
         self.severity = severity
         self.cwe = Cwe(cwe)
@@ -99,12 +100,13 @@ class Issue:
         self.test_id = test_id
         self.lineno = lineno
         self.col_offset = col_offset
+        self.end_col_offset = end_col_offset
         self.linerange = []
 
     def __str__(self):
         return (
             "Issue: '%s' from %s:%s: CWE: %s, Severity: %s Confidence: "
-            "%s at %s:%i"
+            "%s at %s:%i:%i"
         ) % (
             self.text,
             self.test_id,
@@ -114,6 +116,7 @@ class Issue:
             self.confidence,
             self.fname,
             self.lineno,
+            self.col_offset,
         )
 
     def __eq__(self, other):
@@ -206,6 +209,7 @@ class Issue:
             "line_number": self.lineno,
             "line_range": self.linerange,
             "col_offset": self.col_offset,
+            "end_col_offset": self.end_col_offset,
         }
 
         if with_code:
@@ -224,6 +228,7 @@ class Issue:
         self.lineno = data["line_number"]
         self.linerange = data["line_range"]
         self.col_offset = data.get("col_offset", 0)
+        self.end_col_offset = data.get("end_col_offset", 0)
 
 
 def cwe_from_dict(data):
diff --git a/bandit/core/manager.py b/bandit/core/manager.py
index 4e07517..fe20a2d 100644
--- a/bandit/core/manager.py
+++ b/bandit/core/manager.py
@@ -331,12 +331,13 @@ class BanditManager:
             new_files_list.remove(fname)
         except Exception as e:
             LOG.error(
-                "Exception occurred when executing tests against "
-                '%s. Run "bandit --debug %s" to see the full '
-                "traceback.",
+                "Exception occurred when executing tests against %s.",
                 fname,
                 fname,
             )
+            if not LOG.isEnabledFor(logging.DEBUG):
+                LOG.error('Run "bandit --debug %s" to see the full traceback.')
+
             self.skipped.append((fname, "exception while scanning file"))
             new_files_list.remove(fname)
             LOG.debug("  Exception string: %s", e)
diff --git a/bandit/core/node_visitor.py b/bandit/core/node_visitor.py
index 8ee8c32..c2aa393 100644
--- a/bandit/core/node_visitor.py
+++ b/bandit/core/node_visitor.py
@@ -209,6 +209,8 @@ class BanditNodeVisitor:
 
         if hasattr(node, "col_offset"):
             self.context["col_offset"] = node.col_offset
+        if hasattr(node, "end_col_offset"):
+            self.context["end_col_offset"] = node.end_col_offset
 
         self.context["node"] = node
         self.context["linerange"] = b_utils.linerange(node)
diff --git a/bandit/formatters/csv.py b/bandit/formatters/csv.py
index 4043434..6cde187 100644
--- a/bandit/formatters/csv.py
+++ b/bandit/formatters/csv.py
@@ -63,6 +63,7 @@ def report(manager, fileobj, sev_level, conf_level, lines=-1):
             "issue_text",
             "line_number",
             "col_offset",
+            "end_col_offset",
             "line_range",
             "more_info",
         ]
diff --git a/bandit/formatters/custom.py b/bandit/formatters/custom.py
index 5a49e97..712c445 100644
--- a/bandit/formatters/custom.py
+++ b/bandit/formatters/custom.py
@@ -76,6 +76,7 @@ def report(manager, fileobj, sev_level, conf_level, template=None):
         "relpath": lambda issue: os.path.relpath(issue.fname),
         "line": lambda issue: issue.lineno,
         "col": lambda issue: issue.col_offset,
+        "end_col": lambda issue: issue.end_col_offset,
         "test_id": lambda issue: issue.test_id,
         "severity": lambda issue: issue.severity,
         "msg": lambda issue: issue.text,
diff --git a/bandit/plugins/weak_cryptographic_key.py b/bandit/plugins/weak_cryptographic_key.py
index faa0a3e..db91cb2 100644
--- a/bandit/plugins/weak_cryptographic_key.py
+++ b/bandit/plugins/weak_cryptographic_key.py
@@ -111,6 +111,22 @@ def _weak_crypto_key_size_cryptography_io(context, config):
         return _classify_key_size(config, key_type, key_size)
     elif key_type == "EC":
         curve_key_sizes = {
+            "SECT571K1": 571,
+            "SECT571R1": 570,
+            "SECP521R1": 521,
+            "BrainpoolP512R1": 512,
+            "SECT409K1": 409,
+            "SECT409R1": 409,
+            "BrainpoolP384R1": 384,
+            "SECP384R1": 384,
+            "SECT283K1": 283,
+            "SECT283R1": 283,
+            "BrainpoolP256R1": 256,
+            "SECP256K1": 256,
+            "SECP256R1": 256,
+            "SECT233K1": 233,
+            "SECT233R1": 233,
+            "SECP224R1": 224,
             "SECP192R1": 192,
             "SECT163K1": 163,
             "SECT163R2": 163,
diff --git a/doc/source/start.rst b/doc/source/start.rst
index db2b7ab..ca78d74 100644
--- a/doc/source/start.rst
+++ b/doc/source/start.rst
@@ -75,7 +75,7 @@ Version control integration
 Use `pre-commit <https://pre-commit.com/>`_. Once you `have it
 installed <https://pre-commit.com/#install>`_, add this to the
 `.pre-commit-config.yaml` in your repository
-(be sure to update `rev` to point to a real git tag/revision!)::
+(be sure to update `rev` to point to a `real git tag/revision <https://github.com/PyCQA/bandit/releases>`_!)::
 
     repos:
     -   repo: https://github.com/PyCQA/bandit
diff --git a/examples/hashlib_new_insecure_functions.py b/examples/hashlib_new_insecure_functions.py
index 914b305..f7855bd 100644
--- a/examples/hashlib_new_insecure_functions.py
+++ b/examples/hashlib_new_insecure_functions.py
@@ -2,21 +2,19 @@ import hashlib
 
 hashlib.new('md5')
 
-hashlib.new('md4', 'test')
+hashlib.new('md4', b'test')
 
-hashlib.new(name='md5', string='test')
+hashlib.new(name='md5', data=b'test')
 
-hashlib.new('MD4', string='test')
-
-hashlib.new(string='test', name='MD5')
+hashlib.new('MD4', data=b'test')
 
 hashlib.new('sha1')
 
-hashlib.new(string='test', name='SHA1')
+hashlib.new('sha1', data=b'test')
 
-hashlib.new('sha', string='test')
+hashlib.new('sha', data=b'test')
 
-hashlib.new(name='SHA', string='test')
+hashlib.new(name='SHA', data=b'test')
 
 # usedforsecurity arg only availabe in Python 3.9+
 hashlib.new('sha1', usedforsecurity=True)
diff --git a/examples/jsonpickle.py b/examples/jsonpickle.py
new file mode 100644
index 0000000..e8aa290
--- /dev/null
+++ b/examples/jsonpickle.py
@@ -0,0 +1,10 @@
+import jsonpickle
+
+
+pick = jsonpickle.encode({'a': 'b', 'c': 'd'})
+
+print(jsonpickle.decode(pick))
+
+print(jsonpickle.unpickler.decode(pick))
+
+print(jsonpickle.unpickler.Unpickler().restore(pick))


Test patch:
diff --git a/bandit/core/tester.py b/bandit/core/tester.py
index 834fa37..448b917 100644
--- a/bandit/core/tester.py
+++ b/bandit/core/tester.py
@@ -67,6 +67,9 @@ class BanditTester:
                         result.lineno = temp_context["lineno"]
                     result.linerange = temp_context["linerange"]
                     result.col_offset = temp_context["col_offset"]
+                    result.end_col_offset = temp_context.get(
+                        "end_col_offset", 0
+                    )
                     result.test = name
                     if result.test_id == "":
                         result.test_id = test._test_id
diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py
index d39ee79..2005d1e 100644
--- a/tests/functional/test_functional.py
+++ b/tests/functional/test_functional.py
@@ -369,6 +369,14 @@ class FunctionalTests(testtools.TestCase):
         }
         self.check_example("shelve_open.py", expect)
 
+    def test_jsonpickle(self):
+        """Test for the `jsonpickle` module."""
+        expect = {
+            "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 3, "HIGH": 0},
+            "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, "HIGH": 3},
+        }
+        self.check_example("jsonpickle.py", expect)
+
     def test_popen_wrappers(self):
         """Test the `popen2` and `commands` modules."""
         expect = {
@@ -828,13 +836,13 @@ class FunctionalTests(testtools.TestCase):
                     "UNDEFINED": 0,
                     "LOW": 0,
                     "MEDIUM": 0,
-                    "HIGH": 10,
+                    "HIGH": 9,
                 },
                 "CONFIDENCE": {
                     "UNDEFINED": 0,
                     "LOW": 0,
                     "MEDIUM": 0,
-                    "HIGH": 10,
+                    "HIGH": 9,
                 },
             }
         else:
@@ -842,14 +850,14 @@ class FunctionalTests(testtools.TestCase):
                 "SEVERITY": {
                     "UNDEFINED": 0,
                     "LOW": 0,
-                    "MEDIUM": 11,
+                    "MEDIUM": 10,
                     "HIGH": 0,
                 },
                 "CONFIDENCE": {
                     "UNDEFINED": 0,
                     "LOW": 0,
                     "MEDIUM": 0,
-                    "HIGH": 11,
+                    "HIGH": 10,
                 },
             }
         self.check_example("hashlib_new_insecure_functions.py", expect)
diff --git a/tests/unit/core/test_issue.py b/tests/unit/core/test_issue.py
index dd1c72b..629c501 100644
--- a/tests/unit/core/test_issue.py
+++ b/tests/unit/core/test_issue.py
@@ -22,7 +22,7 @@ class IssueTests(testtools.TestCase):
             "Issue: 'Test issue' from B999:bandit_plugin:"
             " CWE: %s,"
             " Severity: MEDIUM "
-            "Confidence: MEDIUM at code.py:1"
+            "Confidence: MEDIUM at code.py:1:8"
         )
 
         self.assertEqual(
@@ -37,10 +37,19 @@ class IssueTests(testtools.TestCase):
         self.assertEqual("bandit_plugin", test_issue_dict["test_name"])
         self.assertEqual("B999", test_issue_dict["test_id"])
         self.assertEqual("MEDIUM", test_issue_dict["issue_severity"])
+        self.assertEqual(
+            {
+                "id": 605,
+                "link": "https://cwe.mitre.org/data/definitions/605.html",
+            },
+            test_issue_dict["issue_cwe"],
+        )
         self.assertEqual("MEDIUM", test_issue_dict["issue_confidence"])
         self.assertEqual("Test issue", test_issue_dict["issue_text"])
         self.assertEqual(1, test_issue_dict["line_number"])
         self.assertEqual([], test_issue_dict["line_range"])
+        self.assertEqual(8, test_issue_dict["col_offset"])
+        self.assertEqual(16, test_issue_dict["end_col_offset"])
 
     def test_issue_filter_severity(self):
         levels = [bandit.LOW, bandit.MEDIUM, bandit.HIGH]
@@ -131,4 +140,7 @@ def _get_issue_instance(
     new_issue.test = "bandit_plugin"
     new_issue.test_id = "B999"
     new_issue.lineno = 1
+    new_issue.col_offset = 8
+    new_issue.end_col_offset = 16
+
     return new_issue
diff --git a/tests/unit/formatters/test_csv.py b/tests/unit/formatters/test_csv.py
index fd9166e..99f4c4b 100644
--- a/tests/unit/formatters/test_csv.py
+++ b/tests/unit/formatters/test_csv.py
@@ -23,6 +23,8 @@ class CsvFormatterTests(testtools.TestCase):
             "filename": self.tmp_fname,
             "lineno": 4,
             "linerange": [4],
+            "col_offset": 8,
+            "end_col_offset": 16,
         }
         self.check_name = "hardcoded_bind_all_interfaces"
         self.issue = issue.Issue(
@@ -36,6 +38,8 @@ class CsvFormatterTests(testtools.TestCase):
         self.issue.fname = self.context["filename"]
         self.issue.lineno = self.context["lineno"]
         self.issue.linerange = self.context["linerange"]
+        self.issue.col_offset = self.context["col_offset"]
+        self.issue.end_col_offset = self.context["end_col_offset"]
         self.issue.test = self.check_name
 
         self.manager.results.append(self.issue)
@@ -62,3 +66,7 @@ class CsvFormatterTests(testtools.TestCase):
             )
             self.assertEqual(self.check_name, data["test_name"])
             self.assertIsNotNone(data["more_info"])
+            self.assertEqual(str(self.issue.col_offset), data["col_offset"])
+            self.assertEqual(
+                str(self.issue.end_col_offset), data["end_col_offset"]
+            )
diff --git a/tests/unit/formatters/test_custom.py b/tests/unit/formatters/test_custom.py
index 908ddb3..5481b71 100644
--- a/tests/unit/formatters/test_custom.py
+++ b/tests/unit/formatters/test_custom.py
@@ -22,6 +22,7 @@ class CustomFormatterTests(testtools.TestCase):
             "lineno": 4,
             "linerange": [4],
             "col_offset": 30,
+            "end_col_offset": 38,
         }
         self.check_name = "hardcoded_bind_all_interfaces"
         self.issue = issue.Issue(
@@ -35,6 +36,7 @@ class CustomFormatterTests(testtools.TestCase):
         self.issue.lineno = self.context["lineno"]
         self.issue.linerange = self.context["linerange"]
         self.issue.col_offset = self.context["col_offset"]
+        self.issue.end_col_offset = self.context["end_col_offset"]
         self.issue.test = self.check_name
 
         self.manager.results.append(self.issue)
@@ -46,13 +48,18 @@ class CustomFormatterTests(testtools.TestCase):
                 tmp_file,
                 self.issue.severity,
                 self.issue.confidence,
-                template="{line},{col},{severity},{msg}",
+                template="{line},{col},{end_col},{severity},{msg}",
             )
 
         with open(self.tmp_fname) as f:
-            reader = csv.DictReader(f, ["line", "col", "severity", "message"])
+            reader = csv.DictReader(
+                f, ["line", "col", "end_col", "severity", "message"]
+            )
             data = next(reader)
             self.assertEqual(str(self.context["lineno"]), data["line"])
             self.assertEqual(str(self.context["col_offset"]), data["col"])
+            self.assertEqual(
+                str(self.context["end_col_offset"]), data["end_col"]
+            )
             self.assertEqual(self.issue.severity, data["severity"])
             self.assertEqual(self.issue.text, data["message"])