diff --git a/bandit/core/node_visitor.py b/bandit/core/node_visitor.py index efa491bd2..8ee8c3244 100644 --- a/bandit/core/node_visitor.py +++ b/bandit/core/node_visitor.py @@ -172,9 +172,7 @@ def visit_Str(self, node): """ self.context["str"] = node.s if not isinstance(node._bandit_parent, ast.Expr): # docstring - self.context["linerange"] = b_utils.linerange_fix( - node._bandit_parent - ) + self.context["linerange"] = b_utils.linerange(node._bandit_parent) self.update_scores(self.tester.run_tests(self.context, "Str")) def visit_Bytes(self, node): @@ -187,9 +185,7 @@ def visit_Bytes(self, node): """ self.context["bytes"] = node.s if not isinstance(node._bandit_parent, ast.Expr): # docstring - self.context["linerange"] = b_utils.linerange_fix( - node._bandit_parent - ) + self.context["linerange"] = b_utils.linerange(node._bandit_parent) self.update_scores(self.tester.run_tests(self.context, "Bytes")) def pre_visit(self, node): @@ -215,7 +211,7 @@ def pre_visit(self, node): self.context["col_offset"] = node.col_offset self.context["node"] = node - self.context["linerange"] = b_utils.linerange_fix(node) + self.context["linerange"] = b_utils.linerange(node) self.context["filename"] = self.fname self.context["file_data"] = self.fdata diff --git a/bandit/core/utils.py b/bandit/core/utils.py index eace35a50..3ac78f54f 100644 --- a/bandit/core/utils.py +++ b/bandit/core/utils.py @@ -220,56 +220,56 @@ def calc_linerange(node): def linerange(node): """Get line number range from a node.""" - if hasattr(node, "_bandit_linerange_stripped"): - lines_minmax = node._bandit_linerange_stripped - return list(range(lines_minmax[0], lines_minmax[1] + 1)) - - strip = { - "body": None, - "orelse": None, - "handlers": None, - "finalbody": None, - } - for key in strip.keys(): - if hasattr(node, key): - strip[key] = getattr(node, key) - setattr(node, key, []) - - lines_min = 9999999999 - lines_max = -1 - if hasattr(node, "lineno"): - lines_min = node.lineno - lines_max = node.lineno - for n in ast.iter_child_nodes(node): - lines_minmax = calc_linerange(n) - lines_min = min(lines_min, lines_minmax[0]) - lines_max = max(lines_max, lines_minmax[1]) - - for key in strip.keys(): - if strip[key] is not None: - setattr(node, key, strip[key]) - - if lines_max == -1: - lines_min = 0 - lines_max = 1 - - node._bandit_linerange_stripped = (lines_min, lines_max) - - return list(range(lines_min, lines_max + 1)) - - -def linerange_fix(node): - """Try and work around a known Python bug with multi-line strings.""" - # deal with multiline strings lineno behavior (Python issue #16806) - lines = linerange(node) - if hasattr(node, "_bandit_sibling") and hasattr( - node._bandit_sibling, "lineno" - ): - start = min(lines) - delta = node._bandit_sibling.lineno - start - if delta > 1: - return list(range(start, node._bandit_sibling.lineno)) - return lines + if sys.version_info >= (3, 8) and hasattr(node, "lineno"): + return list(range(node.lineno, node.end_lineno + 1)) + else: + if hasattr(node, "_bandit_linerange_stripped"): + lines_minmax = node._bandit_linerange_stripped + return list(range(lines_minmax[0], lines_minmax[1] + 1)) + + strip = { + "body": None, + "orelse": None, + "handlers": None, + "finalbody": None, + } + for key in strip.keys(): + if hasattr(node, key): + strip[key] = getattr(node, key) + setattr(node, key, []) + + lines_min = 9999999999 + lines_max = -1 + if hasattr(node, "lineno"): + lines_min = node.lineno + lines_max = node.lineno + for n in ast.iter_child_nodes(node): + lines_minmax = calc_linerange(n) + lines_min = min(lines_min, lines_minmax[0]) + lines_max = max(lines_max, lines_minmax[1]) + + for key in strip.keys(): + if strip[key] is not None: + setattr(node, key, strip[key]) + + if lines_max == -1: + lines_min = 0 + lines_max = 1 + + node._bandit_linerange_stripped = (lines_min, lines_max) + + lines = list(range(lines_min, lines_max + 1)) + + """Try and work around a known Python bug with multi-line strings.""" + # deal with multiline strings lineno behavior (Python issue #16806) + if hasattr(node, "_bandit_sibling") and hasattr( + node._bandit_sibling, "lineno" + ): + start = min(lines) + delta = node._bandit_sibling.lineno - start + if delta > 1: + return list(range(start, node._bandit_sibling.lineno)) + return lines def concat_string(node, stop=None): diff --git a/examples/multiline_statement.py b/examples/multiline_statement.py index b463488d2..278a3e43c 100644 --- a/examples/multiline_statement.py +++ b/examples/multiline_statement.py @@ -4,3 +4,10 @@ "args", shell=True, universal_newlines=True) + +subprocess.check_output( + "/some_command", + "args", + shell=True, + universal_newlines=True +) diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py index 017dc75e7..c45afa58a 100644 --- a/tests/functional/test_functional.py +++ b/tests/functional/test_functional.py @@ -738,17 +738,25 @@ def test_multiline_code(self): ) issues = self.b_mgr.get_issue_list() - self.assertEqual(2, len(issues)) + self.assertEqual(3, len(issues)) self.assertTrue( issues[0].fname.endswith("examples/multiline_statement.py") ) - self.assertEqual(1, issues[0].lineno) - self.assertEqual(list(range(1, 3)), issues[0].linerange) + if sys.version_info >= (3, 8): + self.assertEqual(list(range(1, 2)), issues[0].linerange) + else: + self.assertEqual(list(range(1, 3)), issues[0].linerange) self.assertIn("subprocess", issues[0].get_code()) self.assertEqual(5, issues[1].lineno) self.assertEqual(list(range(3, 6 + 1)), issues[1].linerange) self.assertIn("shell=True", issues[1].get_code()) + self.assertEqual(11, issues[2].lineno) + if sys.version_info >= (3, 8): + self.assertEqual(list(range(8, 13 + 1)), issues[2].linerange) + else: + self.assertEqual(list(range(8, 12 + 1)), issues[2].linerange) + self.assertIn("shell=True", issues[2].get_code()) def test_code_line_numbers(self): self.run_example("binding.py")