This commit is contained in:
Iliyan Angelov
2025-12-01 06:50:10 +02:00
parent 91f51bc6fe
commit 62c1fe5951
4682 changed files with 544807 additions and 31208 deletions

View File

@@ -0,0 +1,244 @@
# 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()