diff --git a/README.rst b/README.rst index 826965856..b382d52c1 100644 --- a/README.rst +++ b/README.rst @@ -457,9 +457,9 @@ using RST:: Under Which Version of Python Should I Install Bandit? ------------------------------------------------------ The answer to this question depends on the project(s) you will be running -Bandit against. If your project is only compatible with Python 2.7, you -should install Bandit to run under Python 2.7. If your project is only -compatible with Python 3.5, then use 3.5 respectively. If your project supports +Bandit against. If your project is only compatible with Python 3.5, you +should install Bandit to run under Python 3.5. If your project is only +compatible with Python 3.8, then use 3.8 respectively. If your project supports both, you *could* run Bandit with both versions but you don't have to. Bandit uses the `ast` module from Python's standard library in order to diff --git a/bandit/core/context.py b/bandit/core/context.py index da90ed26c..139deb55e 100644 --- a/bandit/core/context.py +++ b/bandit/core/context.py @@ -6,8 +6,6 @@ import ast -import six - from bandit.core import utils @@ -211,14 +209,10 @@ def _get_literal_value(self, literal): elif isinstance(literal, ast.Name): literal_value = literal.id - # NOTE(sigmavirus24): NameConstants are only part of the AST in Python - # 3. NameConstants tend to refer to things like True and False. This - # prevents them from being re-assigned in Python 3. - elif six.PY3 and isinstance(literal, ast.NameConstant): + elif isinstance(literal, ast.NameConstant): literal_value = str(literal.value) - # NOTE(sigmavirus24): Bytes are only part of the AST in Python 3 - elif six.PY3 and isinstance(literal, ast.Bytes): + elif isinstance(literal, ast.Bytes): literal_value = literal.s else: diff --git a/bandit/core/extension_loader.py b/bandit/core/extension_loader.py index 05f941102..65b723bc2 100644 --- a/bandit/core/extension_loader.py +++ b/bandit/core/extension_loader.py @@ -6,7 +6,6 @@ import sys -import six from stevedore import extension from bandit.core import utils @@ -75,7 +74,7 @@ def load_blacklists(self, blacklist_namespace): self.blacklist_by_id = {} self.blacklist_by_name = {} - for val in six.itervalues(self.blacklist): + for val in self.blacklist.values(): for b in val: self.blacklist_by_id[b['id']] = b self.blacklist_by_name[b['name']] = b diff --git a/bandit/core/issue.py b/bandit/core/issue.py index 2f9f779fd..98b1d0bb2 100644 --- a/bandit/core/issue.py +++ b/bandit/core/issue.py @@ -9,8 +9,6 @@ import linecache -from six import moves - from bandit.core import constants @@ -84,7 +82,7 @@ def get_code(self, max_lines=3, tabbed=False): lmax = lmin + len(self.linerange) + max_lines - 1 tmplt = "%i\t%s" if tabbed else "%i %s" - for line in moves.xrange(lmin, lmax): + for line in range(lmin, lmax): text = linecache.getline(self.fname, line) if isinstance(text, bytes): diff --git a/bandit/core/manager.py b/bandit/core/manager.py index 4a20de5d8..466670c8a 100644 --- a/bandit/core/manager.py +++ b/bandit/core/manager.py @@ -13,8 +13,6 @@ import tokenize import traceback -import six - from bandit.core import constants as b_constants from bandit.core import extension_loader from bandit.core import issue @@ -280,10 +278,7 @@ def _parse_file(self, fname, fdata, new_files_list): else: try: fdata.seek(0) - if six.PY2: - tokens = tokenize.generate_tokens(fdata.readline) - else: - tokens = tokenize.tokenize(fdata.readline) + tokens = tokenize.tokenize(fdata.readline) nosec_lines = set( lineno for toktype, tokval, (lineno, _), _, _ in tokens if toktype == tokenize.COMMENT and diff --git a/bandit/formatters/html.py b/bandit/formatters/html.py index 29c008b70..525706f4e 100644 --- a/bandit/formatters/html.py +++ b/bandit/formatters/html.py @@ -142,19 +142,14 @@ """ from __future__ import absolute_import +from html import escape as html_escape import logging import sys -import six - from bandit.core import docs_utils from bandit.core import test_properties from bandit.formatters import utils -if not six.PY2: - from html import escape as html_escape -else: - from cgi import escape as html_escape LOG = logging.getLogger(__name__) @@ -377,8 +372,8 @@ def report(manager, fileobj, sev_level, conf_level, lines=-1): with fileobj: wrapped_file = utils.wrap_file_object(fileobj) - wrapped_file.write(utils.convert_file_contents(header_block)) - wrapped_file.write(utils.convert_file_contents(report_contents)) + wrapped_file.write(header_block) + wrapped_file.write(report_contents) if fileobj.name != sys.stdout.name: LOG.info("HTML output written to file: %s", fileobj.name) diff --git a/bandit/formatters/text.py b/bandit/formatters/text.py index dbb2cf5d4..1c77a31a4 100644 --- a/bandit/formatters/text.py +++ b/bandit/formatters/text.py @@ -156,7 +156,7 @@ def report(manager, fileobj, sev_level, conf_level, lines=-1): with fileobj: wrapped_file = utils.wrap_file_object(fileobj) - wrapped_file.write(utils.convert_file_contents(result)) + wrapped_file.write(result) if fileobj.name != sys.stdout.name: LOG.info("Text output written to file: %s", fileobj.name) diff --git a/bandit/formatters/utils.py b/bandit/formatters/utils.py index 172fa8a77..eee762f12 100644 --- a/bandit/formatters/utils.py +++ b/bandit/formatters/utils.py @@ -5,8 +5,6 @@ import io -import six - def wrap_file_object(fileobj): """Handle differences in Python 2 and 3 around writing bytes.""" @@ -24,10 +22,3 @@ def wrap_file_object(fileobj): # Finally, we've determined that the fileobj passed in cannot handle text, # so we use TextIOWrapper to handle the conversion for us. return io.TextIOWrapper(fileobj) - - -def convert_file_contents(text): - """Convert text to built-in strings on Python 2.""" - if not six.PY2: - return text - return str(text.encode('utf-8')) diff --git a/bandit/formatters/xml.py b/bandit/formatters/xml.py index a21e80024..522571595 100644 --- a/bandit/formatters/xml.py +++ b/bandit/formatters/xml.py @@ -35,8 +35,6 @@ import sys from xml.etree import cElementTree as ET -import six - from bandit.core import docs_utils LOG = logging.getLogger(__name__) @@ -71,10 +69,7 @@ def report(manager, fileobj, sev_level, conf_level, lines=-1): tree = ET.ElementTree(root) if fileobj.name == sys.stdout.name: - if six.PY2: - fileobj = sys.stdout - else: - fileobj = sys.stdout.buffer + fileobj = sys.stdout.buffer elif fileobj.mode == 'w': fileobj.close() fileobj = open(fileobj.name, "wb") diff --git a/bandit/plugins/django_xss.py b/bandit/plugins/django_xss.py index 17e134607..fd8bc635e 100644 --- a/bandit/plugins/django_xss.py +++ b/bandit/plugins/django_xss.py @@ -6,8 +6,6 @@ import ast -import six - import bandit from bandit.core import test_properties as test @@ -45,28 +43,13 @@ def is_assigned(self, node): return assigned assigned = self.is_assigned_in(node.body) elif isinstance(node, ast.With): - if six.PY2: - if node.optional_vars.id == self.var_name.id: + for withitem in node.items: + var_id = getattr(withitem.optional_vars, 'id', None) + if var_id == self.var_name.id: assigned = node else: assigned = self.is_assigned_in(node.body) - else: - for withitem in node.items: - var_id = getattr(withitem.optional_vars, 'id', None) - if var_id == self.var_name.id: - assigned = node - else: - assigned = self.is_assigned_in(node.body) - elif six.PY2 and isinstance(node, ast.TryFinally): - assigned = [] - assigned.extend(self.is_assigned_in(node.body)) - assigned.extend(self.is_assigned_in(node.finalbody)) - elif six.PY2 and isinstance(node, ast.TryExcept): - assigned = [] - assigned.extend(self.is_assigned_in(node.body)) - assigned.extend(self.is_assigned_in(node.handlers)) - assigned.extend(self.is_assigned_in(node.orelse)) - elif not six.PY2 and isinstance(node, ast.Try): + elif isinstance(node, ast.Try): assigned = [] assigned.extend(self.is_assigned_in(node.body)) assigned.extend(self.is_assigned_in(node.handlers)) @@ -103,8 +86,7 @@ def evaluate_var(xss_var, parent, until, ignore_nodes=None): if isinstance(xss_var, ast.Name): if isinstance(parent, ast.FunctionDef): for name in parent.args.args: - arg_name = name.id if six.PY2 else name.arg - if arg_name == xss_var.id: + if name.arg == xss_var.id: return False # Params are not secure analyser = DeepAssignation(xss_var, ignore_nodes) @@ -150,15 +132,11 @@ def evaluate_call(call, parent, ignore_nodes=None): if isinstance(call, ast.Call) and isinstance(call.func, ast.Attribute): if isinstance(call.func.value, ast.Str) and call.func.attr == 'format': evaluate = True - if call.keywords or (six.PY2 and call.kwargs): + if call.keywords: evaluate = False # TODO(??) get support for this if evaluate: args = list(call.args) - if six.PY2 and call.starargs and isinstance(call.starargs, - (ast.List, ast.Tuple)): - args.extend(call.starargs.elts) - num_secure = 0 for arg in args: if isinstance(arg, ast.Str): @@ -173,7 +151,7 @@ def evaluate_call(call, parent, ignore_nodes=None): num_secure += 1 else: break - elif not six.PY2 and isinstance(arg, ast.Starred) and isinstance( + elif isinstance(arg, ast.Starred) and isinstance( arg.value, (ast.List, ast.Tuple)): args.extend(arg.value.elts) num_secure += 1 @@ -192,19 +170,13 @@ def transform2call(var): new_call = ast.Call() new_call.args = [] new_call.args = [] - if six.PY2: - new_call.starargs = None new_call.keywords = None - if six.PY2: - new_call.kwargs = None new_call.lineno = var.lineno new_call.func = ast.Attribute() new_call.func.value = var.left new_call.func.attr = 'format' if isinstance(var.right, ast.Tuple): new_call.args = var.right.elts - elif six.PY2 and isinstance(var.right, ast.Dict): - new_call.kwargs = var.right else: new_call.args = [var.right] return new_call @@ -225,8 +197,7 @@ def check_risk(node): is_param = False if isinstance(parent, ast.FunctionDef): for name in parent.args.args: - arg_name = name.id if six.PY2 else name.arg - if arg_name == xss_var.id: + if name.arg == xss_var.id: is_param = True break diff --git a/bandit/plugins/exec.py b/bandit/plugins/exec.py index 3d7d8c2d7..ee5dfbba2 100644 --- a/bandit/plugins/exec.py +++ b/bandit/plugins/exec.py @@ -18,7 +18,7 @@ >> Issue: Use of exec detected. Severity: Medium Confidence: High - Location: ./examples/exec-py2.py:2 + Location: ./examples/exec.py:2 1 exec("do evil") 2 exec "do evil" @@ -32,8 +32,6 @@ .. versionadded:: 0.9.0 """ -import six - import bandit from bandit.core import test_properties as test @@ -46,14 +44,8 @@ def exec_issue(): ) -if six.PY2: - @test.checks('Exec') - @test.test_id('B102') - def exec_used(context): +@test.checks('Call') +@test.test_id('B102') +def exec_used(context): + if context.call_function_name_qual == 'exec': return exec_issue() -else: - @test.checks('Call') - @test.test_id('B102') - def exec_used(context): - if context.call_function_name_qual == 'exec': - return exec_issue() diff --git a/bandit/plugins/general_bad_file_permissions.py b/bandit/plugins/general_bad_file_permissions.py index 551b25446..66893dd00 100644 --- a/bandit/plugins/general_bad_file_permissions.py +++ b/bandit/plugins/general_bad_file_permissions.py @@ -25,14 +25,14 @@ >> Issue: Probable insecure usage of temp file/directory. Severity: Medium Confidence: Medium - Location: ./examples/os-chmod-py2.py:15 + Location: ./examples/os-chmod.py:15 14 os.chmod('/etc/hosts', 0o777) 15 os.chmod('/tmp/oh_hai', 0x1ff) 16 os.chmod('/etc/passwd', stat.S_IRWXU) >> Issue: Chmod setting a permissive mask 0777 on file (key_file). Severity: High Confidence: High - Location: ./examples/os-chmod-py2.py:17 + Location: ./examples/os-chmod.py:17 16 os.chmod('/etc/passwd', stat.S_IRWXU) 17 os.chmod(key_file, 0o777) 18 diff --git a/bandit/plugins/general_hardcoded_password.py b/bandit/plugins/general_hardcoded_password.py index 2a44c4cc1..dabd8cd47 100644 --- a/bandit/plugins/general_hardcoded_password.py +++ b/bandit/plugins/general_hardcoded_password.py @@ -6,7 +6,6 @@ import ast import re -import sys import bandit from bandit.core import test_properties as test @@ -205,10 +204,6 @@ def hardcoded_password_default(context): # go through all (param, value)s and look for candidates for key, val in zip(context.node.args.args, defs): - py3_is_arg = True - if sys.version_info.major > 2: - py3_is_arg = isinstance(key, ast.arg) - if isinstance(key, ast.Name) or py3_is_arg: - check = key.arg if sys.version_info.major > 2 else key.id # Py3 - if isinstance(val, ast.Str) and RE_CANDIDATES.search(check): + if isinstance(key, (ast.Name, ast.arg)): + if isinstance(val, ast.Str) and RE_CANDIDATES.search(key.arg): return _report(val.s) diff --git a/bandit/plugins/injection_shell.py b/bandit/plugins/injection_shell.py index fcbd61be3..a99cfd700 100644 --- a/bandit/plugins/injection_shell.py +++ b/bandit/plugins/injection_shell.py @@ -7,8 +7,6 @@ import ast import re -import six - import bandit from bandit.core import test_properties as test @@ -91,7 +89,7 @@ def has_shell(context): result = bool(val.keys) elif isinstance(val, ast.Name) and val.id in ['False', 'None']: result = False - elif not six.PY2 and isinstance(val, ast.NameConstant): + elif isinstance(val, ast.NameConstant): result = val.value else: result = True diff --git a/examples/exec-py2.py b/examples/exec-py2.py deleted file mode 100644 index ae36c573f..000000000 --- a/examples/exec-py2.py +++ /dev/null @@ -1,2 +0,0 @@ -exec("do evil") -exec "do evil" \ No newline at end of file diff --git a/examples/exec-py3.py b/examples/exec.py similarity index 100% rename from examples/exec-py3.py rename to examples/exec.py diff --git a/examples/os-chmod-py2.py b/examples/os-chmod-py2.py deleted file mode 100644 index 847512aff..000000000 --- a/examples/os-chmod-py2.py +++ /dev/null @@ -1,17 +0,0 @@ -import os -import stat - -keyfile = 'foo' - -os.chmod('/etc/passwd', 0227) -os.chmod('/etc/passwd', 07) -os.chmod('/etc/passwd', 0664) -os.chmod('/etc/passwd', 0777) -os.chmod('/etc/passwd', 0o770) -os.chmod('/etc/passwd', 0o776) -os.chmod('/etc/passwd', 0o760) -os.chmod('~/.bashrc', 511) -os.chmod('/etc/hosts', 0o777) -os.chmod('/tmp/oh_hai', 0x1ff) -os.chmod('/etc/passwd', stat.S_IRWXU) -os.chmod(key_file, 0o777) diff --git a/examples/os-chmod-py3.py b/examples/os-chmod.py similarity index 100% rename from examples/os-chmod-py3.py rename to examples/os-chmod.py diff --git a/requirements.txt b/requirements.txt index c32fa59c7..749ba561c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,5 @@ # process, which may cause wedges in the gate later. GitPython>=1.0.1 # BSD License (3 clause) PyYAML>=5.3.1 # MIT -six>=1.10.0 # MIT stevedore>=1.20.0 # Apache-2.0 colorama>=0.3.9;platform_system=="Windows" # BSD License (3 clause) diff --git a/setup.py b/setup.py index fe565f172..3e1a8c235 100644 --- a/setup.py +++ b/setup.py @@ -5,13 +5,6 @@ # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools -# In python < 2.7.4, a lazy loading of package `pbr` will break -# setuptools if some other modules registered functions in `atexit`. -# solution from: http://bugs.python.org/issue15881#msg170215 -try: - import multiprocessing # noqa -except ImportError: - pass setuptools.setup( python_requires='>=3.5', diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py index f8b373bbf..04b0ed5aa 100644 --- a/tests/functional/test_functional.py +++ b/tests/functional/test_functional.py @@ -7,7 +7,6 @@ import os import sys -import six import testtools from bandit.core import config as b_config @@ -150,21 +149,12 @@ def test_mark_safe(self): def test_exec(self): '''Test the `exec` example.''' - filename = 'exec-{}.py' - if six.PY2: - filename = filename.format('py2') - expect = { - 'SEVERITY': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 2, 'HIGH': 0}, - 'CONFIDENCE': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 0, - 'HIGH': 2} - } - else: - filename = filename.format('py3') - expect = { - 'SEVERITY': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 1, 'HIGH': 0}, - 'CONFIDENCE': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 0, - 'HIGH': 1} - } + filename = 'exec.py' + expect = { + 'SEVERITY': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 1, 'HIGH': 0}, + 'CONFIDENCE': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 0, + 'HIGH': 1} + } self.check_example(filename, expect) def test_hardcoded_passwords(self): @@ -286,11 +276,7 @@ def test_subdirectory_okay(self): def test_os_chmod(self): '''Test setting file permissions.''' - filename = 'os-chmod-{}.py' - if six.PY2: - filename = filename.format('py2') - else: - filename = filename.format('py3') + filename = 'os-chmod.py' expect = { 'SEVERITY': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 2, 'HIGH': 8}, 'CONFIDENCE': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 1, 'HIGH': 9} diff --git a/tests/functional/test_runtime.py b/tests/functional/test_runtime.py index b77a606b9..6398dc0a7 100644 --- a/tests/functional/test_runtime.py +++ b/tests/functional/test_runtime.py @@ -5,7 +5,6 @@ import os import subprocess -import six import testtools @@ -103,12 +102,7 @@ def test_example_nonsense2(self): ) self.assertEqual(0, retcode) self.assertIn("Files skipped (1):", output) - if six.PY2: - self.assertIn("nonsense2.py (exception while scanning file)", - output) - else: - self.assertIn("nonsense2.py (syntax error while parsing AST", - output) + self.assertIn("nonsense2.py (syntax error while parsing AST", output) def test_example_imports(self): (retcode, output) = self._test_example(['bandit', ], ['imports.py', ]) diff --git a/tests/unit/core/test_context.py b/tests/unit/core/test_context.py index e6df83e28..afa1e02b0 100644 --- a/tests/unit/core/test_context.py +++ b/tests/unit/core/test_context.py @@ -7,7 +7,6 @@ import ast import mock -import six import testtools from bandit.core import context @@ -167,15 +166,9 @@ def test__get_literal_value(self): expected = value.id self.assertEqual(expected, new_context._get_literal_value(value)) - if six.PY3: - value = ast.NameConstant(True) - expected = str(value.value) - self.assertEqual(expected, new_context._get_literal_value(value)) - - if six.PY3: - value = ast.Bytes(b'spam') - expected = value.s - self.assertEqual(expected, new_context._get_literal_value(value)) + value = ast.Bytes(b'spam') + expected = value.s + self.assertEqual(expected, new_context._get_literal_value(value)) self.assertIsNone(new_context._get_literal_value(None)) diff --git a/tests/unit/core/test_meta_ast.py b/tests/unit/core/test_meta_ast.py index 065ed5a6d..6ef61b99a 100644 --- a/tests/unit/core/test_meta_ast.py +++ b/tests/unit/core/test_meta_ast.py @@ -2,7 +2,6 @@ # # SPDX-License-Identifier: Apache-2.0 -import six import testtools from bandit.core import meta_ast @@ -28,4 +27,4 @@ def test_add_node(self): def test_str(self): node = self.b_meta_ast.nodes[self.node_id] expected = 'Node: %s\n\t%s\nLength: 1\n' % (self.node_id, node) - self.assertEqual(expected, six.text_type(self.b_meta_ast)) + self.assertEqual(expected, str(self.b_meta_ast)) diff --git a/tests/unit/formatters/test_csv.py b/tests/unit/formatters/test_csv.py index 1d459711e..ee45fff62 100644 --- a/tests/unit/formatters/test_csv.py +++ b/tests/unit/formatters/test_csv.py @@ -5,7 +5,6 @@ import csv import tempfile -import six import testtools import bandit @@ -44,14 +43,13 @@ def test_report(self): with open(self.tmp_fname) as f: reader = csv.DictReader(f) - data = six.next(reader) + data = next(reader) self.assertEqual(self.tmp_fname, data['filename']) self.assertEqual(self.issue.severity, data['issue_severity']) self.assertEqual(self.issue.confidence, data['issue_confidence']) self.assertEqual(self.issue.text, data['issue_text']) - self.assertEqual(six.text_type(self.context['lineno']), - data['line_number']) - self.assertEqual(six.text_type(self.context['linerange']), + self.assertEqual(str(self.context['lineno']), data['line_number']) + self.assertEqual(str(self.context['linerange']), data['line_range']) self.assertEqual(self.check_name, data['test_name']) self.assertIsNotNone(data['more_info']) diff --git a/tests/unit/formatters/test_custom.py b/tests/unit/formatters/test_custom.py index b71b8c3fa..3a903ac97 100644 --- a/tests/unit/formatters/test_custom.py +++ b/tests/unit/formatters/test_custom.py @@ -3,7 +3,6 @@ import csv import tempfile -import six import testtools import bandit @@ -46,10 +45,8 @@ def test_report(self): with open(self.tmp_fname) as f: reader = csv.DictReader(f, ['line', 'col', 'severity', 'message']) - data = six.next(reader) - self.assertEqual(six.text_type(self.context['lineno']), - data['line']) - self.assertEqual(six.text_type(self.context['col_offset']), - data['col']) + data = next(reader) + self.assertEqual(str(self.context['lineno']), data['line']) + self.assertEqual(str(self.context['col_offset']), data['col']) self.assertEqual(self.issue.severity, data['severity']) self.assertEqual(self.issue.text, data['message'])