content: # 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 f"{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([f"\t{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(f"\tTotal issues (by {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.now(datetime.timezone.utc)
              )
          )
  
          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()
  