diff --git a/bandit/cli/config_generator.py b/bandit/cli/config_generator.py index e46867f5e..5b941cd4b 100644 --- a/bandit/cli/config_generator.py +++ b/bandit/cli/config_generator.py @@ -158,11 +158,11 @@ def main(): for skip in skips: if not extension_loader.MANAGER.check_id(skip): - raise RuntimeError("unknown ID in skips: %s" % skip) + raise RuntimeError(f"unknown ID in skips: {skip}") for test in tests: if not extension_loader.MANAGER.check_id(test): - raise RuntimeError("unknown ID in tests: %s" % test) + raise RuntimeError(f"unknown ID in tests: {test}") tpl = "# {0} : {1}" test_list = [ diff --git a/bandit/cli/main.py b/bandit/cli/main.py index 47588859d..119380b28 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -371,9 +371,8 @@ def main(): parser.add_argument( "--version", action="version", - version="%(prog)s {version}\n python version = {python}".format( - version=bandit.__version__, python=python_ver - ), + version=f"%(prog)s {bandit.__version__}\n" + f" python version = {python_ver}", ) parser.set_defaults(debug=False) @@ -387,7 +386,7 @@ def main(): blacklist_info = [] for a in extension_mgr.blacklist.items(): for b in a[1]: - blacklist_info.append("{}\t{}".format(b["id"], b["name"])) + blacklist_info.append(f"{b['id']}\t{b['name']}") plugin_list = "\n\t".join(sorted(set(plugin_info + blacklist_info))) dedent_text = textwrap.dedent( diff --git a/bandit/core/config.py b/bandit/core/config.py index 236f357c5..f0c9acb0e 100644 --- a/bandit/core/config.py +++ b/bandit/core/config.py @@ -19,7 +19,6 @@ from bandit.core import extension_loader from bandit.core import utils - LOG = logging.getLogger(__name__) diff --git a/bandit/core/context.py b/bandit/core/context.py index 1ecfb4495..76b50923a 100644 --- a/bandit/core/context.py +++ b/bandit/core/context.py @@ -34,7 +34,7 @@ def __repr__(self): :return: A string representation of the object """ - return "" % self._context + return f"" @property def call_args(self): diff --git a/bandit/core/docs_utils.py b/bandit/core/docs_utils.py index 54ca16510..5a5575b5e 100644 --- a/bandit/core/docs_utils.py +++ b/bandit/core/docs_utils.py @@ -16,11 +16,7 @@ def get_url(bid): info = extension_loader.MANAGER.plugins_by_id.get(bid) if info is not None: - return "{}plugins/{}_{}.html".format( - base_url, - bid.lower(), - info.plugin.__name__, - ) + return f"{base_url}plugins/{bid.lower()}_{info.plugin.__name__}.html" info = extension_loader.MANAGER.blacklist_by_id.get(bid) if info is not None: diff --git a/bandit/core/extension_loader.py b/bandit/core/extension_loader.py index 7730ee8e4..e3af9769b 100644 --- a/bandit/core/extension_loader.py +++ b/bandit/core/extension_loader.py @@ -42,7 +42,7 @@ def test_has_id(plugin): if not hasattr(plugin.plugin, "_test_id"): # logger not setup yet, so using print print( - "WARNING: Test '%s' has no ID, skipping." % plugin.name, + f"WARNING: Test '{plugin.name}' has no ID, skipping.", file=sys.stderr, ) return False @@ -82,16 +82,16 @@ def validate_profile(self, profile): """Validate that everything in the configured profiles looks good.""" for inc in profile["include"]: if not self.check_id(inc): - raise ValueError("Unknown test found in profile: %s" % inc) + raise ValueError(f"Unknown test found in profile: {inc}") for exc in profile["exclude"]: if not self.check_id(exc): - raise ValueError("Unknown test found in profile: %s" % exc) + raise ValueError(f"Unknown test found in profile: {exc}") union = set(profile["include"]) & set(profile["exclude"]) if len(union) > 0: raise ValueError( - "Non-exclusive include/exclude test sets: %s" % union + f"Non-exclusive include/exclude test sets: {union}" ) def check_id(self, test): diff --git a/bandit/core/manager.py b/bandit/core/manager.py index e82f67165..02fed5c30 100644 --- a/bandit/core/manager.py +++ b/bandit/core/manager.py @@ -23,7 +23,6 @@ from bandit.core import node_visitor as b_node_visitor from bandit.core import test_set as b_test_set - LOG = logging.getLogger(__name__) NOSEC_COMMENT = re.compile(r"#\s*nosec:?\s*(?P[^#]+)?#?") NOSEC_COMMENT_TESTS = re.compile(r"(?:(B\d+|[a-z_]+),?)+", re.IGNORECASE) @@ -195,8 +194,8 @@ def output_results( except Exception as e: raise RuntimeError( - "Unable to output report using '%s' formatter: " - "%s" % (output_format, str(e)) + f"Unable to output report using " + f"'{output_format}' formatter: {str(e)}" ) def discover_files(self, targets, recursive=False, excluded_paths=""): diff --git a/bandit/core/meta_ast.py b/bandit/core/meta_ast.py index bf94540c6..d26461c0c 100644 --- a/bandit/core/meta_ast.py +++ b/bandit/core/meta_ast.py @@ -5,7 +5,6 @@ import collections import logging - LOG = logging.getLogger(__name__) @@ -40,7 +39,7 @@ def __str__(self): """ tmpstr = "" for k, v in self.nodes.items(): - tmpstr += "Node: %s\n" % k - tmpstr += "\t%s\n" % str(v) - tmpstr += "Length: %s\n" % len(self.nodes) + tmpstr += f"Node: {k}\n" + tmpstr += f"\t{str(v)}\n" + tmpstr += f"Length: {len(self.nodes)}\n" return tmpstr diff --git a/bandit/core/node_visitor.py b/bandit/core/node_visitor.py index e721db64f..26cdb2471 100644 --- a/bandit/core/node_visitor.py +++ b/bandit/core/node_visitor.py @@ -10,7 +10,6 @@ from bandit.core import tester as b_tester from bandit.core import utils as b_utils - LOG = logging.getLogger(__name__) diff --git a/bandit/core/test_set.py b/bandit/core/test_set.py index 8fa2ef236..1e7dd0d8c 100644 --- a/bandit/core/test_set.py +++ b/bandit/core/test_set.py @@ -8,7 +8,6 @@ from bandit.core import blacklisting from bandit.core import extension_loader - LOG = logging.getLogger(__name__) diff --git a/bandit/core/tester.py b/bandit/core/tester.py index 042b9400d..af5ffdae9 100644 --- a/bandit/core/tester.py +++ b/bandit/core/tester.py @@ -84,9 +84,9 @@ def run_tests(self, raw_context, checktype): LOG.debug("skipped, nosec without test number") self.metrics.note_nosec() continue - elif result.test_id in nosec_tests_to_skip: + if result.test_id in nosec_tests_to_skip: LOG.debug( - "skipped, nosec for test %s" % result.test_id + f"skipped, nosec for test {result.test_id}" ) self.metrics.note_skipped_test() continue @@ -152,7 +152,7 @@ def _get_nosecs_from_contexts(self, context, test_result=None): @staticmethod def report_error(test, context, error): what = "Bandit internal error running: " - what += "%s " % test + what += f"{test} " what += "on file %s at line %i: " % ( context._context["filename"], context._context["lineno"], diff --git a/bandit/core/utils.py b/bandit/core/utils.py index a7e1c2b60..8d55b9c9e 100644 --- a/bandit/core/utils.py +++ b/bandit/core/utils.py @@ -140,8 +140,7 @@ def get_module_qualname_from_path(path): (head, tail) = os.path.split(path) if head == "" or tail == "": raise InvalidModulePath( - 'Invalid python file path: "%s"' - " Missing path or file name" % (path) + f'Invalid python file path: "{path}" Missing path or file name' ) qname = [os.path.splitext(tail)[0]] @@ -369,7 +368,7 @@ def check_ast_node(name): except AttributeError: # nosec(tkelsey): catching expected exception pass - raise TypeError("Error: %s is not a valid node type in AST" % name) + raise TypeError(f"Error: {name} is not a valid node type in AST") def get_nosec(nosec_lines, context): diff --git a/bandit/formatters/custom.py b/bandit/formatters/custom.py index 712c44571..cb07152f7 100644 --- a/bandit/formatters/custom.py +++ b/bandit/formatters/custom.py @@ -33,7 +33,6 @@ from bandit.core import test_properties - LOG = logging.getLogger(__name__) @@ -141,10 +140,7 @@ def get_similar_tag(tag): markers = ["", ":", "!"] msg_parsed_template_list.append( ["{"] - + [ - "%s" % (m + p) if p else "" - for m, p in zip(markers, params) - ] + + [f"{m + p}" if p else "" for m, p in zip(markers, params)] + ["}"] ) diff --git a/bandit/formatters/html.py b/bandit/formatters/html.py index f2ee3f234..fb09f835f 100644 --- a/bandit/formatters/html.py +++ b/bandit/formatters/html.py @@ -154,7 +154,6 @@ from bandit.core import test_properties from bandit.formatters import utils - LOG = logging.getLogger(__name__) diff --git a/bandit/formatters/screen.py b/bandit/formatters/screen.py index b3c6e268d..7b0917b53 100644 --- a/bandit/formatters/screen.py +++ b/bandit/formatters/screen.py @@ -67,7 +67,7 @@ def header(text, *args): - return "{}{}{}".format(COLOR["HEADER"], (text % args), COLOR["DEFAULT"]) + return f"{COLOR['HEADER']}{text % args}{COLOR['DEFAULT']}" def get_verbose_details(manager): @@ -81,7 +81,7 @@ def get_verbose_details(manager): ] ) bits.append(header("Files excluded (%i):", len(manager.excluded_files))) - bits.extend(["\t%s" % fname for fname in manager.excluded_files]) + bits.extend([f"\t{fname}" for fname in manager.excluded_files]) return "\n".join([str(bit) for bit in bits]) @@ -89,7 +89,7 @@ def get_metrics(manager): bits = [] bits.append(header("\nRun metrics:")) for (criteria, _) in constants.CRITERIA: - bits.append("\tTotal issues (by %s):" % (criteria.lower())) + bits.append(f"\tTotal issues (by {criteria.lower()}):") for rank in constants.RANKING: bits.append( "\t\t%s: %s" diff --git a/bandit/formatters/text.py b/bandit/formatters/text.py index 16dcc4913..5930c75b3 100644 --- a/bandit/formatters/text.py +++ b/bandit/formatters/text.py @@ -46,7 +46,7 @@ def get_verbose_details(manager): bits = [] - bits.append("Files in scope (%i):" % len(manager.files_list)) + bits.append(f"Files in scope ({len(manager.files_list)}):") tpl = "\t%s (score: {SEVERITY: %i, CONFIDENCE: %i})" bits.extend( [ @@ -54,8 +54,8 @@ def get_verbose_details(manager): for (item, score) in zip(manager.files_list, manager.scores) ] ) - bits.append("Files excluded (%i):" % len(manager.excluded_files)) - bits.extend(["\t%s" % fname for fname in manager.excluded_files]) + bits.append(f"Files excluded ({len(manager.excluded_files)}):") + bits.extend([f"\t{fname}" for fname in manager.excluded_files]) return "\n".join([bit for bit in bits]) @@ -63,7 +63,7 @@ def get_metrics(manager): bits = [] bits.append("\nRun metrics:") for (criteria, _) in constants.CRITERIA: - bits.append("\tTotal issues (by %s):" % (criteria.lower())) + bits.append(f"\tTotal issues (by {criteria.lower()}):") for rank in constants.RANKING: bits.append( "\t\t%s: %s" @@ -81,8 +81,7 @@ def _output_issue_str( # returns a list of lines that should be added to the existing lines list bits = [] bits.append( - "%s>> Issue: [%s:%s] %s" - % (indent, issue.test_id, issue.test, issue.text) + f"{indent}>> Issue: [{issue.test_id}:{issue.test}] {issue.text}" ) bits.append( @@ -162,7 +161,7 @@ def report(manager, fileobj, sev_level, conf_level, lines=-1): bits = [] if not manager.quiet or manager.results_count(sev_level, conf_level): - bits.append("Run started:%s" % datetime.datetime.utcnow()) + bits.append(f"Run started:{datetime.datetime.utcnow()}") if manager.verbose: bits.append(get_verbose_details(manager)) @@ -187,7 +186,7 @@ def report(manager, fileobj, sev_level, conf_level, lines=-1): skipped = manager.get_skipped() bits.append(get_metrics(manager)) - bits.append("Files skipped (%i):" % len(skipped)) + bits.append(f"Files skipped ({len(skipped)}):") bits.extend(["\t%s (%s)" % skip for skip in skipped]) result = "\n".join([bit for bit in bits]) + "\n" diff --git a/bandit/plugins/general_hardcoded_password.py b/bandit/plugins/general_hardcoded_password.py index 9a162b485..cc3e7d09d 100644 --- a/bandit/plugins/general_hardcoded_password.py +++ b/bandit/plugins/general_hardcoded_password.py @@ -9,7 +9,6 @@ from bandit.core import issue from bandit.core import test_properties as test - RE_WORDS = "(pas+wo?r?d|pass(phrase)?|pwd|token|secrete?)" RE_CANDIDATES = re.compile( "(^{0}$|_{0}_|^{0}_|_{0}$)".format(RE_WORDS), re.IGNORECASE @@ -21,7 +20,7 @@ def _report(value): severity=bandit.LOW, confidence=bandit.MEDIUM, cwe=issue.Cwe.HARD_CODED_PASSWORD, - text=("Possible hardcoded password: '%s'" % value), + text=f"Possible hardcoded password: '{value}'", ) diff --git a/bandit/plugins/hashlib_insecure_functions.py b/bandit/plugins/hashlib_insecure_functions.py index 30627a060..15603e9b1 100644 --- a/bandit/plugins/hashlib_insecure_functions.py +++ b/bandit/plugins/hashlib_insecure_functions.py @@ -47,7 +47,6 @@ from bandit.core import issue from bandit.core import test_properties as test - WEAK_HASHES = ("md4", "md5", "sha", "sha1") diff --git a/bandit/plugins/injection_shell.py b/bandit/plugins/injection_shell.py index 19c31d209..249ec48bf 100644 --- a/bandit/plugins/injection_shell.py +++ b/bandit/plugins/injection_shell.py @@ -9,7 +9,6 @@ from bandit.core import issue from bandit.core import test_properties as test - # yuck, regex: starts with a windows drive letter (eg C:) # or one of our path delimeter characters (/, \, .) full_path_match = re.compile(r"^(?:[A-Za-z](?=\:)|[\\\/\.])") diff --git a/bandit/plugins/injection_wildcard.py b/bandit/plugins/injection_wildcard.py index c4209fb25..94d03b30a 100644 --- a/bandit/plugins/injection_wildcard.py +++ b/bandit/plugins/injection_wildcard.py @@ -124,7 +124,7 @@ def linux_commands_wildcard_injection(context, config): argument_string = "" if isinstance(call_argument, list): for li in call_argument: - argument_string = argument_string + " %s" % li + argument_string = argument_string + f" {li}" elif isinstance(call_argument, str): argument_string = call_argument diff --git a/doc/source/conf.py b/doc/source/conf.py index 6e376759d..8cd247d6e 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -65,7 +65,7 @@ html_theme_options = {} # Output file base name for HTML help builder. -htmlhelp_basename = "%sdoc" % project +htmlhelp_basename = f"{project}doc" # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass @@ -73,8 +73,8 @@ latex_documents = [ ( "index", - "%s.tex" % project, - "%s Documentation" % project, + f"{project}.tex", + f"{project} Documentation", "PyCQA", "manual", ), diff --git a/pylintrc b/pylintrc index f5810628f..17952a2cc 100644 --- a/pylintrc +++ b/pylintrc @@ -6,15 +6,22 @@ # C0325: Parens are required on print in py3x # F0401: Imports are check by other linters # W0511: TODOs in code comments are fine. -# W0142: *args and **kwargs are fine. # W0622: Redefining id is fine. # TODO(browne): fix these in the future # C0103: invalid-name +# C0114: Missing module docstring +# C0115: Missing class docstring +# C0116: Missing function or method docstring # C0201: consider-iterating-dictionary -# C1801: len-as-condition +# C0206: Consider iterating with .items() +# C0209: Foramtting a regular string which could be an f-string +# C0413: wrong-import-position +# C0415: Import outside toplevel +# C1802: Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +# E0611: No name in module # E1101: no-member -# E1111: assignment-from-no-return +# R0801: Similar lines in 2 files # R0902: too-many-instance-attributes # R0912: too-many-branches # R0913: too-many-arguments @@ -23,18 +30,26 @@ # R1702: too-many-nested-blocks # R1705: no-else-return # R1710: inconsistent-return-statements -# W0110: deprecated-lambda -# W0141: bad-builtin +# R1714: Consider merging these comparisons with 'in' +# R1721: Unnecessary use of a comprehension +# R1732: Consider using 'with' for resource-allocating operations +# R1734: Consider using [] instead of list() +# R1735: use-dict-literal +# W0105: String statement has no effect # W0201: attribute-defined-outside-init # W0212: protected-access -# W0401: wildcard-import +# W0246: Useless parent or super() delegation # W0603: global-statement +# W0612: Unused variable # W0613: unused-argument # W0621: redefined-outer-name -# W0703: broad-except +# W0707: Consider explicitly re-raising +# W0718: Catching too general exception Exception # W1201: logging-not-lazy -# W1505: deprecated-method -disable=C0111,C0201,C0301,C0325,C1801,F0401,W0511,W0142,W0622,C0103,E1101,E1111,R0902,R0912,R0913,R0914,R0915,R1702,R1705,R1710,W0110,W0141,W0201,W0401,W0603,W0212,W0613,W0621,W0703,W1201,W1505 +# W1203: Use lazy % or % formatting in logging functions +# W1404: Implicit string concatenation found in call +# W1514: Using open without explicitly specifying an encoding +disable=C0103,C0114,C0115,C0116,C0201,C0206,C0209,C0301,C0413,C0415,C1802,F0401,W0511,W0622,E0611,E1101,R0801,R0902,R0912,R0913,R0914,R0915,R1702,R1705,R1710,R1714,R1721,R1732,R1734,R1735,W0105,W0201,W0212,W0246,W0603,W0612,W0613,W0621,W0707,W0718,W1201,W1203,W1404,W1514 [Basic] # Variable names can be 1 to 31 characters long, with lowercase and underscores @@ -72,6 +87,9 @@ min-similarity-lines=10 # Ignore comments when computing similarities. ignore-comments=yes +# We don't need to do pylint on the examples, too many false positives +ignore-paths=examples + # Ignore docstrings when computing similarities. ignore-docstrings=yes diff --git a/setup.py b/setup.py index 3d1911d58..1350bc193 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,6 @@ # SPDX-License-Identifier: Apache-2.0 import setuptools - setuptools.setup( python_requires=">=3.7", setup_requires=["pbr>=2.0.0"], pbr=True ) diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py index 65596d839..bc8689396 100644 --- a/tests/functional/test_functional.py +++ b/tests/functional/test_functional.py @@ -835,18 +835,18 @@ def test_baseline_filter(self): "exposes the Werkzeug debugger and allows the execution " "of arbitrary code." ) - json = """{{ + json = f"""{{ "results": [ {{ "code": "...", - "filename": "{}/examples/flask_debug.py", + "filename": "{os.getcwd()}/examples/flask_debug.py", "issue_confidence": "MEDIUM", "issue_severity": "HIGH", "issue_cwe": {{ "id": 94, "link": "https://cwe.mitre.org/data/definitions/94.html" }}, - "issue_text": "{}", + "issue_text": "{issue_text}", "line_number": 10, "col_offset": 0, "line_range": [ @@ -857,10 +857,7 @@ def test_baseline_filter(self): }} ] }} - """.format( - os.getcwd(), - issue_text, - ) + """ self.b_mgr.populate_baseline(json) self.run_example("flask_debug.py") diff --git a/tests/unit/cli/test_baseline.py b/tests/unit/cli/test_baseline.py index 5a6113b45..68c692551 100644 --- a/tests/unit/cli/test_baseline.py +++ b/tests/unit/cli/test_baseline.py @@ -10,8 +10,7 @@ import git import testtools -import bandit.cli.baseline as baseline - +from bandit.cli import baseline config = """ include: @@ -258,7 +257,7 @@ def test_initialize_existing_report_file(self): os.chdir(repo_directory) # create an existing version of output report file - existing_report = "{}.{}".format(baseline.report_basename, "txt") + existing_report = f"{baseline.report_basename}.txt" with open(existing_report, "wt") as fd: fd.write(self.temp_file_contents) diff --git a/tests/unit/core/test_config.py b/tests/unit/core/test_config.py index 3684023ab..b7eef4c63 100644 --- a/tests/unit/core/test_config.py +++ b/tests/unit/core/test_config.py @@ -78,11 +78,10 @@ def setUp(self): self.example_subkey = uuid.uuid4().hex self.example_subvalue = uuid.uuid4().hex sample_yaml = textwrap.dedent( + f""" + {self.example_key}: + {self.example_subkey}: {self.example_subvalue} """ - %s: - %s: %s - """ - % (self.example_key, self.example_subkey, self.example_subvalue) ) f = self.useFixture(TempFile(sample_yaml)) diff --git a/tests/unit/core/test_util.py b/tests/unit/core/test_util.py index 374191c7d..99ba34f31 100644 --- a/tests/unit/core/test_util.py +++ b/tests/unit/core/test_util.py @@ -192,7 +192,6 @@ def test_get_module_qualname_from_path_rel_missingend(self): def test_get_module_qualname_from_path_rel_syms(self): """Test get_module_qualname_from_path with symbolic relative paths.""" - name = b_utils.get_module_qualname_from_path( os.path.join( self.reltempdir, "syms", "a", "bsym", "c", "test_typical.py" diff --git a/tests/unit/formatters/test_screen.py b/tests/unit/formatters/test_screen.py index 54b5b921f..b560247cd 100644 --- a/tests/unit/formatters/test_screen.py +++ b/tests/unit/formatters/test_screen.py @@ -40,14 +40,9 @@ def _template(_issue, _indent_val, _code, _color): _issue.severity.capitalize(), _issue.confidence.capitalize(), ), - "{} CWE: {}".format( - _indent_val, - _issue.cwe, - ), - "{} More Info: {}".format( - _indent_val, - docs_utils.get_url(_issue.test_id), - ), + f"{_indent_val} CWE: {_issue.cwe}", + f"{_indent_val} More Info: " + f"{docs_utils.get_url(_issue.test_id)}", "{} Location: {}:{}:{}{}".format( _indent_val, _issue.fname, diff --git a/tests/unit/formatters/test_text.py b/tests/unit/formatters/test_text.py index 1f8d036ef..a868c3468 100644 --- a/tests/unit/formatters/test_text.py +++ b/tests/unit/formatters/test_text.py @@ -37,9 +37,8 @@ def _template(_issue, _indent_val, _code): _issue.confidence.capitalize(), ), f"{_indent_val} CWE: {_issue.cwe}", - "{} More Info: {}".format( - _indent_val, docs_utils.get_url(_issue.test_id) - ), + f"{_indent_val} More Info: " + f"{docs_utils.get_url(_issue.test_id)}", "{} Location: {}:{}:{}".format( _indent_val, _issue.fname, _issue.lineno, _issue.col_offset ), @@ -148,7 +147,7 @@ def test_report_nobaseline(self, get_issue_list): "binding.py (score: ", "CONFIDENCE: 1", "SEVERITY: 1", - "CWE: %s" % str(issue.Cwe(issue.Cwe.MULTIPLE_BINDS)), + f"CWE: {str(issue.Cwe(issue.Cwe.MULTIPLE_BINDS))}", "Files excluded (1):", "def.py", "Undefined: 1",