| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- # Copyright (c) 2015 Hewlett Packard Enterprise
- #
- # SPDX-License-Identifier: Apache-2.0
- r"""
- ================
- Screen formatter
- ================
- This formatter outputs the issues as color coded text to screen.
- :Example:
- .. code-block:: none
- >> Issue: [B506: yaml_load] Use of unsafe yaml load. Allows
- instantiation of arbitrary objects. Consider yaml.safe_load().
- Severity: Medium Confidence: High
- CWE: CWE-20 (https://cwe.mitre.org/data/definitions/20.html)
- More Info: https://bandit.readthedocs.io/en/latest/
- Location: examples/yaml_load.py:5
- 4 ystr = yaml.dump({'a' : 1, 'b' : 2, 'c' : 3})
- 5 y = yaml.load(ystr)
- 6 yaml.dump(y)
- .. versionadded:: 0.9.0
- .. versionchanged:: 1.5.0
- New field `more_info` added to output
- .. versionchanged:: 1.7.3
- New field `CWE` added to output
- """
- import datetime
- import logging
- import sys
- from bandit.core import constants
- from bandit.core import docs_utils
- from bandit.core import test_properties
- IS_WIN_PLATFORM = sys.platform.startswith("win32")
- COLORAMA = False
- # This fixes terminal colors not displaying properly on Windows systems.
- # Colorama will intercept any ANSI escape codes and convert them to the
- # proper Windows console API calls to change text color.
- if IS_WIN_PLATFORM:
- try:
- import colorama
- except ImportError:
- pass
- else:
- COLORAMA = True
- LOG = logging.getLogger(__name__)
- COLOR = {
- "DEFAULT": "\033[0m",
- "HEADER": "\033[95m",
- "LOW": "\033[94m",
- "MEDIUM": "\033[93m",
- "HIGH": "\033[91m",
- }
- def header(text, *args):
- return "{}{}{}".format(COLOR["HEADER"], (text % args), COLOR["DEFAULT"])
- def get_verbose_details(manager):
- bits = []
- bits.append(header("Files in scope (%i):", len(manager.files_list)))
- tpl = "\t%s (score: {SEVERITY: %i, CONFIDENCE: %i})"
- bits.extend(
- [
- tpl % (item, sum(score["SEVERITY"]), sum(score["CONFIDENCE"]))
- for (item, score) in zip(manager.files_list, manager.scores)
- ]
- )
- bits.append(header("Files excluded (%i):", len(manager.excluded_files)))
- bits.extend(["\t%s" % fname for fname in manager.excluded_files])
- return "\n".join([str(bit) for bit in bits])
- def get_metrics(manager):
- bits = []
- bits.append(header("\nRun metrics:"))
- for (criteria, _) in constants.CRITERIA:
- bits.append("\tTotal issues (by %s):" % (criteria.lower()))
- for rank in constants.RANKING:
- bits.append(
- "\t\t%s: %s"
- % (
- rank.capitalize(),
- manager.metrics.data["_totals"][f"{criteria}.{rank}"],
- )
- )
- return "\n".join([str(bit) for bit in bits])
- def _output_issue_str(
- issue, indent, show_lineno=True, show_code=True, lines=-1
- ):
- # returns a list of lines that should be added to the existing lines list
- bits = []
- bits.append(
- "%s%s>> Issue: [%s:%s] %s"
- % (
- indent,
- COLOR[issue.severity],
- issue.test_id,
- issue.test,
- issue.text,
- )
- )
- bits.append(
- "%s Severity: %s Confidence: %s"
- % (
- indent,
- issue.severity.capitalize(),
- issue.confidence.capitalize(),
- )
- )
- bits.append(f"{indent} CWE: {str(issue.cwe)}")
- bits.append(f"{indent} More Info: {docs_utils.get_url(issue.test_id)}")
- bits.append(
- "%s Location: %s:%s:%s%s"
- % (
- indent,
- issue.fname,
- issue.lineno if show_lineno else "",
- issue.col_offset if show_lineno else "",
- COLOR["DEFAULT"],
- )
- )
- if show_code:
- bits.extend(
- [indent + line for line in issue.get_code(lines, True).split("\n")]
- )
- return "\n".join([bit for bit in bits])
- def get_results(manager, sev_level, conf_level, lines):
- bits = []
- issues = manager.get_issue_list(sev_level, conf_level)
- baseline = not isinstance(issues, list)
- candidate_indent = " " * 10
- if not len(issues):
- return "\tNo issues identified."
- for issue in issues:
- # if not a baseline or only one candidate we know the issue
- if not baseline or len(issues[issue]) == 1:
- bits.append(_output_issue_str(issue, "", lines=lines))
- # otherwise show the finding and the candidates
- else:
- bits.append(
- _output_issue_str(
- issue, "", show_lineno=False, show_code=False
- )
- )
- bits.append("\n-- Candidate Issues --")
- for candidate in issues[issue]:
- bits.append(
- _output_issue_str(candidate, candidate_indent, lines=lines)
- )
- bits.append("\n")
- bits.append("-" * 50)
- return "\n".join([bit for bit in bits])
- def do_print(bits):
- # needed so we can mock this stuff
- print("\n".join([bit for bit in bits]))
- @test_properties.accepts_baseline
- def report(manager, fileobj, sev_level, conf_level, lines=-1):
- """Prints discovered issues formatted for screen reading
- This makes use of VT100 terminal codes for colored text.
- :param manager: the bandit manager object
- :param fileobj: The output file object, which may be sys.stdout
- :param sev_level: Filtering severity level
- :param conf_level: Filtering confidence level
- :param lines: Number of lines to report, -1 for all
- """
- if IS_WIN_PLATFORM and COLORAMA:
- colorama.init()
- bits = []
- if not manager.quiet or manager.results_count(sev_level, conf_level):
- bits.append(header("Run started:%s", datetime.datetime.utcnow()))
- if manager.verbose:
- bits.append(get_verbose_details(manager))
- bits.append(header("\nTest results:"))
- bits.append(get_results(manager, sev_level, conf_level, lines))
- bits.append(header("\nCode scanned:"))
- bits.append(
- "\tTotal lines of code: %i"
- % (manager.metrics.data["_totals"]["loc"])
- )
- bits.append(
- "\tTotal lines skipped (#nosec): %i"
- % (manager.metrics.data["_totals"]["nosec"])
- )
- bits.append(get_metrics(manager))
- skipped = manager.get_skipped()
- bits.append(header("Files skipped (%i):", len(skipped)))
- bits.extend(["\t%s (%s)" % skip for skip in skipped])
- do_print(bits)
- if fileobj.name != sys.stdout.name:
- LOG.info(
- "Screen formatter output was not written to file: %s, "
- "consider '-f txt'",
- fileobj.name,
- )
- if IS_WIN_PLATFORM and COLORAMA:
- colorama.deinit()
|