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,71 @@
from collections import namedtuple
from typing import List, Dict, Any, Optional, Tuple
from safety.formatter import FormatterAPI
from safety.util import get_basic_announcements
class BareReport(FormatterAPI):
"""
Bare report, for command line tools.
"""
def render_vulnerabilities(self, announcements: List[Dict[str, Any]], vulnerabilities: List[Any],
remediations: Any, full: bool, packages: List[Any], fixes: Tuple = ()) -> str:
"""
Renders vulnerabilities in a bare format.
Args:
announcements (List[Dict[str, Any]]): List of announcements.
vulnerabilities (List[Any]): List of vulnerabilities.
remediations (Any): Remediation data.
full (bool): Flag indicating full output.
packages (List[Any]): List of packages.
fixes (Tuple, optional): Tuple of fixes.
Returns:
str: Rendered vulnerabilities.
"""
parsed_announcements = []
Announcement = namedtuple("Announcement", ["name"])
for announcement in get_basic_announcements(announcements, include_local=False):
normalized_message = "-".join(announcement.get('message', 'none').lower().split())
parsed_announcements.append(Announcement(name=normalized_message))
announcements_to_render = [announcement.name for announcement in parsed_announcements]
affected_packages = list(set([v.package_name for v in vulnerabilities if not v.ignored]))
return " ".join(announcements_to_render + affected_packages)
def render_licenses(self, announcements: List[Dict[str, Any]], packages_licenses: List[Dict[str, Any]]) -> str:
"""
Renders licenses in a bare format.
Args:
announcements (List[Dict[str, Any]]): List of announcements.
packages_licenses (List[Dict[str, Any]]): List of package licenses.
Returns:
str: Rendered licenses.
"""
parsed_announcements = []
for announcement in get_basic_announcements(announcements):
normalized_message = "-".join(announcement.get('message', 'none').lower().split())
parsed_announcements.append({'license': normalized_message})
announcements_to_render = [announcement.get('license') for announcement in parsed_announcements]
licenses = list(set([pkg_li.get('license') for pkg_li in packages_licenses]))
sorted_licenses = sorted(licenses)
return " ".join(announcements_to_render + sorted_licenses)
def render_announcements(self, announcements: List[Dict[str, Any]]) -> None:
"""
Renders announcements in a bare format.
Args:
announcements (List[Dict[str, Any]]): List of announcements.
"""
print('render_announcements bare')

View File

@@ -0,0 +1,58 @@
import logging
from typing import List, Dict, Tuple, Optional
from safety.formatter import FormatterAPI
from safety.formatters.json import build_json_report
from safety.output_utils import get_report_brief_info, parse_html
from safety.util import get_basic_announcements
LOG = logging.getLogger(__name__)
class HTMLReport(FormatterAPI):
"""
HTML report formatter for when the output is input for something else.
"""
def render_vulnerabilities(self, announcements: List[Dict], vulnerabilities: List[Dict], remediations: Dict,
full: bool, packages: List[Dict], fixes: Tuple = ()) -> Optional[str]:
"""
Renders vulnerabilities in HTML format.
Args:
announcements (List[Dict]): List of announcements.
vulnerabilities (List[Dict]): List of vulnerabilities.
remediations (Dict): Remediation data.
full (bool): Flag indicating full output.
packages (List[Dict]): List of packages.
fixes (Tuple, optional): Tuple of fixes.
Returns:
str: Rendered HTML vulnerabilities report.
"""
LOG.debug(
f'HTML Output, Rendering {len(vulnerabilities)} vulnerabilities, {len(remediations)} package '
f'remediations with full_report: {full}')
report = build_json_report(announcements, vulnerabilities, remediations, packages)
return parse_html(kwargs={"json_data": report})
def render_licenses(self, announcements: List[Dict], licenses: List[Dict]) -> None:
"""
Renders licenses in HTML format.
Args:
announcements (List[Dict]): List of announcements.
licenses (List[Dict]): List of licenses.
"""
pass
def render_announcements(self, announcements: List[Dict]) -> None:
"""
Renders announcements in HTML format.
Args:
announcements (List[Dict]): List of announcements.
"""
pass

View File

@@ -0,0 +1,236 @@
# type: ignore
# TODO: Handle typing issues
import logging
import json as json_parser
from collections import defaultdict
from typing import Iterable, List, Dict, Any
from safety.formatter import FormatterAPI
from safety.models import SafetyEncoder
from safety.output_utils import get_report_brief_info
from safety.safety import find_vulnerabilities_fixed
from safety.util import get_basic_announcements, SafetyContext
LOG = logging.getLogger(__name__)
def build_json_report(
announcements: List[Dict],
vulnerabilities: List[Dict],
remediations: Dict[str, Any],
packages: List[Any],
) -> Dict[str, Any]:
"""
Build a JSON report for vulnerabilities, remediations, and packages.
Args:
announcements (List[Dict]): List of announcements.
vulnerabilities (List[Dict]): List of vulnerabilities.
remediations (Dict[str, Any]): Remediation data.
packages (List[Any]): List of packages.
Returns:
Dict[str, Any]: JSON report.
"""
vulns_ignored = [vuln.to_dict() for vuln in vulnerabilities if vuln.ignored]
vulns = [vuln.to_dict() for vuln in vulnerabilities if not vuln.ignored]
report = get_report_brief_info(
as_dict=True,
report_type=1,
vulnerabilities_found=len(vulns),
vulnerabilities_ignored=len(vulns_ignored),
remediations_recommended=remediations,
)
if "using_sentence" in report:
del report["using_sentence"]
remed = {}
for k, v in remediations.items():
if k not in remed:
remed[k] = {"requirements": v}
remed[k]["current_version"] = None
remed[k]["vulnerabilities_found"] = None
remed[k]["recommended_version"] = None
remed[k]["other_recommended_versions"] = []
remed[k]["more_info_url"] = None
return {
"report_meta": report,
"scanned_packages": {p.name: p.to_dict(short_version=True) for p in packages},
"affected_packages": {v.pkg.name: v.pkg.to_dict() for v in vulnerabilities},
"announcements": [
{"type": item.get("type"), "message": item.get("message")}
for item in get_basic_announcements(announcements)
],
"vulnerabilities": vulns,
"ignored_vulnerabilities": vulns_ignored,
"remediations": remed,
}
class JsonReport(FormatterAPI):
"""Json report, for when the output is input for something else"""
VERSIONS = ("0.5", "1.1")
def __init__(self, version="1.1", **kwargs):
"""
Initialize JsonReport with the specified version.
Args:
version (str): Report version.
"""
super().__init__(**kwargs)
self.version: str = version if version in self.VERSIONS else "1.1"
def render_vulnerabilities(
self,
announcements: List[Dict],
vulnerabilities: List[Dict],
remediations: Dict[str, Any],
full: bool,
packages: List[Any],
fixes: Iterable = (),
) -> str:
"""
Render vulnerabilities in JSON format.
Args:
announcements (List[Dict]): List of announcements.
vulnerabilities (List[Dict]): List of vulnerabilities.
remediations (Dict[str, Any]): Remediation data.
full (bool): Flag indicating full output.
packages (List[Any]): List of packages.
fixes (Iterable, optional): Iterable of fixes.
Returns:
str: Rendered JSON vulnerabilities report.
"""
if self.version == "0.5":
from safety.formatters.schemas.zero_five import VulnerabilitySchemaV05
return json_parser.dumps(
VulnerabilitySchemaV05().dump(obj=vulnerabilities, many=True), indent=4
)
remediations_recommended = len(remediations.keys())
LOG.debug(
"Rendering %s vulnerabilities, %s package remediations with full_report: %s",
len(vulnerabilities),
remediations_recommended,
full,
)
report = build_json_report(
announcements, vulnerabilities, remediations, packages
)
template = self.__render_fixes(report, fixes)
return json_parser.dumps(template, indent=4, cls=SafetyEncoder)
def render_licenses(self, announcements: List[Dict], licenses: List[Dict]) -> str:
"""
Render licenses in JSON format.
Args:
announcements (List[Dict]): List of announcements.
licenses (List[Dict]): List of licenses.
Returns:
str: Rendered JSON licenses report.
"""
unique_license_types = set([lic["license"] for lic in licenses])
report = get_report_brief_info(
as_dict=True, report_type=2, licenses_found=len(unique_license_types)
)
template = {
"report_meta": report,
"announcements": get_basic_announcements(announcements),
"licenses": licenses,
}
return json_parser.dumps(template, indent=4)
def render_announcements(self, announcements: List[Dict]) -> str:
"""
Render announcements in JSON format.
Args:
announcements (List[Dict]): List of announcements.
Returns:
str: Rendered JSON announcements.
"""
return json_parser.dumps(
{"announcements": get_basic_announcements(announcements)}, indent=4
)
def __render_fixes(
self, scan_template: Dict[str, Any], fixes: Iterable
) -> Dict[str, Any]:
"""
Render fixes and update the scan template with remediations information.
Args:
scan_template (Dict[str, Any]): Initial scan template.
fixes (Iterable): Iterable of fixes.
Returns:
Dict[str, Any]: Updated scan template with remediations.
"""
applied = defaultdict(lambda: defaultdict(lambda: defaultdict(dict)))
skipped = defaultdict(lambda: defaultdict(lambda: defaultdict(dict)))
fixes_applied = []
total_applied = 0
for fix in fixes:
if fix.status == "APPLIED":
total_applied += 1
applied[fix.applied_at][fix.package][fix.previous_spec] = {
"previous_version": str(fix.previous_version),
"previous_spec": str(fix.previous_spec),
"updated_version": str(fix.updated_version),
"update_type": str(fix.update_type),
"fix_type": fix.fix_type,
}
fixes_applied.append(fix)
else:
skipped[fix.applied_at][fix.package][fix.previous_spec] = {
"scanned_version": str(fix.previous_version)
if fix.previous_version
else None,
"scanned_spec": str(fix.previous_spec)
if fix.previous_spec
else None,
"skipped_reason": fix.status,
}
vulnerabilities = scan_template.get("vulnerabilities", {})
remediation_mode = "NON_INTERACTIVE"
if SafetyContext().params.get("prompt_mode", False):
remediation_mode = "INTERACTIVE"
scan_template["report_meta"].update(
{
"remediations_attempted": len(fixes),
"remediations_completed": total_applied,
"remediation_mode": remediation_mode,
}
)
scan_template["remediations_results"] = {
"vulnerabilities_fixed": find_vulnerabilities_fixed(
vulnerabilities, fixes_applied
),
"remediations_applied": applied,
"remediations_skipped": skipped,
}
return scan_template

View File

@@ -0,0 +1,95 @@
from typing import List, Tuple
from marshmallow import Schema, fields as fields_, post_dump
from importlib.metadata import version as dep_version
from packaging import version
marshmallow_version = version.parse(dep_version("marshmallow"))
if marshmallow_version >= version.parse("4.0.0"):
POST_DUMP_KWARGS = {"pass_collection": True}
else:
POST_DUMP_KWARGS = {"pass_many": True}
post_dump_with_kwargs = lambda fn: post_dump(fn, **POST_DUMP_KWARGS) # noqa: E731
class CVSSv2(Schema):
"""
Schema for CVSSv2 data.
Attributes:
base_score (fields_.Int): Base score of the CVSSv2.
impact_score (fields_.Int): Impact score of the CVSSv2.
vector_string (fields_.Str): Vector string of the CVSSv2.
"""
base_score = fields_.Int()
impact_score = fields_.Int()
vector_string = fields_.Str()
class Meta(Schema.Meta):
ordered = True
class CVSSv3(Schema):
"""
Schema for CVSSv3 data.
Attributes:
base_score (fields_.Int): Base score of the CVSSv3.
base_severity (fields_.Str): Base severity of the CVSSv3.
impact_score (fields_.Int): Impact score of the CVSSv3.
vector_string (fields_.Str): Vector string of the CVSSv3.
"""
base_score = fields_.Int()
base_severity = fields_.Str()
impact_score = fields_.Int()
vector_string = fields_.Str()
class Meta(Schema.Meta):
ordered = True
class VulnerabilitySchemaV05(Schema):
"""
Legacy JSON report schema used in Safety 1.10.3.
Attributes:
package_name (fields_.Str): Name of the vulnerable package.
vulnerable_spec (fields_.Str): Vulnerable specification of the package.
version (fields_.Str): Version of the package.
advisory (fields_.Str): Advisory details for the vulnerability.
vulnerability_id (fields_.Str): ID of the vulnerability.
cvssv2 (Optional[CVSSv2]): CVSSv2 details of the vulnerability.
cvssv3 (Optional[CVSSv3]): CVSSv3 details of the vulnerability.
"""
package_name = fields_.Str()
vulnerable_spec = fields_.Str()
version = fields_.Str(attribute="pkg.version")
advisory = fields_.Str()
vulnerability_id = fields_.Str()
cvssv2 = fields_.Nested(CVSSv2, attribute="severity.cvssv2")
cvssv3 = fields_.Nested(CVSSv3, attribute="severity.cvssv3")
class Meta(Schema.Meta):
ordered = True
@post_dump_with_kwargs
def wrap_with_envelope(self, data, many, **kwargs) -> List[Tuple]:
"""
Wraps the dumped data with an envelope.
Args:
data (List[Dict[str, Any]]): The data to be wrapped.
many (bool): Indicates if multiple objects are being dumped.
**kwargs (Any): Additional keyword arguments.
Returns:
List[Tuple]: The wrapped data.
"""
return [tuple(d.values()) for d in data]

View File

@@ -0,0 +1,199 @@
import click
from safety.formatter import FormatterAPI
from safety.output_utils import build_announcements_section_content, format_long_text, \
add_empty_line, format_vulnerability, get_final_brief, \
build_report_brief_section, format_license, get_final_brief_license, build_remediation_section, \
build_primary_announcement, get_specifier_range_info, format_unpinned_vulnerabilities
from safety.util import get_primary_announcement, get_basic_announcements, get_terminal_size, \
is_ignore_unpinned_mode
from collections import defaultdict
from typing import List, Dict, Any, Tuple
class ScreenReport(FormatterAPI):
DIVIDER_SECTIONS = '+' + '=' * (get_terminal_size().columns - 2) + '+'
REPORT_BANNER = DIVIDER_SECTIONS + '\n' + r"""
/$$$$$$ /$$
/$$__ $$ | $$
/$$$$$$$ /$$$$$$ | $$ \__//$$$$$$ /$$$$$$ /$$ /$$
/$$_____/ |____ $$| $$$$ /$$__ $$|_ $$_/ | $$ | $$
| $$$$$$ /$$$$$$$| $$_/ | $$$$$$$$ | $$ | $$ | $$
\____ $$ /$$__ $$| $$ | $$_____/ | $$ /$$| $$ | $$
/$$$$$$$/| $$$$$$$| $$ | $$$$$$$ | $$$$/| $$$$$$$
|_______/ \_______/|__/ \_______/ \___/ \____ $$
/$$ | $$
| $$$$$$/
by safetycli.com \______/
""" + DIVIDER_SECTIONS
ANNOUNCEMENTS_HEADING = format_long_text(click.style('ANNOUNCEMENTS', bold=True))
def __build_announcements_section(self, announcements: List[Dict]) -> List[str]:
"""
Build the announcements section of the report.
Args:
announcements (List[Dict]): List of announcement dictionaries.
Returns:
List[str]: Formatted announcements section.
"""
announcements_section = []
basic_announcements = get_basic_announcements(announcements)
if basic_announcements:
announcements_content = build_announcements_section_content(basic_announcements)
announcements_section = [add_empty_line(), self.ANNOUNCEMENTS_HEADING, add_empty_line(),
announcements_content, add_empty_line(), self.DIVIDER_SECTIONS]
return announcements_section
def render_vulnerabilities(self, announcements: List[Dict], vulnerabilities: List[Dict], remediations: Dict[str, Any],
full: bool, packages: List[Dict], fixes: Tuple = ()) -> str:
"""
Render the vulnerabilities section of the report.
Args:
announcements (List[Dict]): List of announcement dictionaries.
vulnerabilities (List[Dict]): List of vulnerability dictionaries.
remediations (Dict[str, Any]): Remediation data.
full (bool): Flag indicating full report.
packages (List[Dict]): List of package dictionaries.
fixes (Tuple, optional): Iterable of fixes.
Returns:
str: Rendered vulnerabilities report.
"""
announcements_section = self.__build_announcements_section(announcements)
primary_announcement = get_primary_announcement(announcements)
remediation_section = build_remediation_section(remediations)
end_content = []
if primary_announcement:
end_content = [add_empty_line(),
build_primary_announcement(primary_announcement, columns=get_terminal_size().columns),
self.DIVIDER_SECTIONS]
table = []
ignored = {}
total_ignored = 0
unpinned_packages = defaultdict(list)
styled_vulns = []
for n, vuln in enumerate(vulnerabilities):
if vuln.ignored:
total_ignored += 1
ignored[vuln.package_name] = ignored.get(vuln.package_name, 0) + 1
if is_ignore_unpinned_mode(version=vuln.analyzed_version) and not full:
unpinned_packages[vuln.package_name].append(vuln)
continue
styled_vulns.append(format_vulnerability(vuln, full))
table.extend(format_unpinned_vulnerabilities(unpinned_packages))
table.extend(styled_vulns)
report_brief_section = build_report_brief_section(primary_announcement=primary_announcement, report_type=1,
vulnerabilities_found=max(0, len(vulnerabilities)-total_ignored),
vulnerabilities_ignored=total_ignored,
remediations_recommended=remediations)
if vulnerabilities:
# Add a space between warning and brief, when all the vulnerabilities are ignored.
if not styled_vulns:
table.append('')
final_brief = get_final_brief(len(vulnerabilities), remediations, ignored, total_ignored)
return "\n".join(
[ScreenReport.REPORT_BANNER] + announcements_section + [report_brief_section,
add_empty_line(),
self.DIVIDER_SECTIONS,
format_long_text(
click.style('VULNERABILITIES REPORTED',
bold=True)),
self.DIVIDER_SECTIONS,
add_empty_line(),
"\n\n".join(table),
add_empty_line(),
self.DIVIDER_SECTIONS] +
remediation_section + ['', final_brief, '', self.DIVIDER_SECTIONS] + end_content
)
else:
content = format_long_text(click.style("No known security vulnerabilities reported.", bold=True, fg='green'))
return "\n".join(
[ScreenReport.REPORT_BANNER] + announcements_section + [report_brief_section,
self.DIVIDER_SECTIONS,
add_empty_line(),
content,
add_empty_line(),
self.DIVIDER_SECTIONS] +
end_content
)
def render_licenses(self, announcements: List[Dict], licenses: List[Dict]) -> str:
"""
Render the licenses section of the report.
Args:
announcements (List[Dict]): List of announcement dictionaries.
licenses (List[Dict]): List of license dictionaries.
Returns:
str: Rendered licenses report.
"""
unique_license_types = set([lic['license'] for lic in licenses])
report_brief_section = build_report_brief_section(primary_announcement=get_primary_announcement(announcements),
report_type=2, licenses_found=len(unique_license_types))
announcements_section = self.__build_announcements_section(announcements)
if not licenses:
content = format_long_text(click.style("No packages licenses found.", bold=True, fg='red'))
return "\n".join(
[ScreenReport.REPORT_BANNER] + announcements_section + [report_brief_section,
self.DIVIDER_SECTIONS,
add_empty_line(),
content,
add_empty_line(),
self.DIVIDER_SECTIONS]
)
table = []
for license in licenses:
table.append(format_license(license))
final_brief = get_final_brief_license(unique_license_types)
return "\n".join(
[ScreenReport.REPORT_BANNER] + announcements_section + [report_brief_section,
add_empty_line(),
self.DIVIDER_SECTIONS,
format_long_text(
click.style('LICENSES FOUND',
bold=True, fg='yellow')),
self.DIVIDER_SECTIONS,
add_empty_line(),
"\n".join(table),
final_brief,
add_empty_line(),
self.DIVIDER_SECTIONS]
)
def render_announcements(self, announcements: List[Dict]) -> List[str]:
"""
Render the announcements section of the report.
Args:
announcements (List[Dict]): List of announcement dictionaries.
Returns:
str: Rendered announcements section.
"""
return self.__build_announcements_section(announcements)

View File

@@ -0,0 +1,198 @@
from collections import defaultdict
import click
from safety.formatter import FormatterAPI
from safety.output_utils import build_announcements_section_content, format_vulnerability, \
build_report_brief_section, get_final_brief_license, add_empty_line, get_final_brief, build_remediation_section, \
build_primary_announcement, format_unpinned_vulnerabilities
from safety.util import get_primary_announcement, get_basic_announcements, is_ignore_unpinned_mode, \
get_remediations_count
from typing import List, Dict, Tuple, Any
class TextReport(FormatterAPI):
"""Basic report, intented to be used for terminals with < 80 columns"""
SMALL_DIVIDER_SECTIONS = '+' + '=' * 78 + '+'
TEXT_REPORT_BANNER = SMALL_DIVIDER_SECTIONS + '\n' + r"""
/$$$$$$ /$$
/$$__ $$ | $$
/$$$$$$$ /$$$$$$ | $$ \__//$$$$$$ /$$$$$$ /$$ /$$
/$$_____/ |____ $$| $$$$ /$$__ $$|_ $$_/ | $$ | $$
| $$$$$$ /$$$$$$$| $$_/ | $$$$$$$$ | $$ | $$ | $$
\____ $$ /$$__ $$| $$ | $$_____/ | $$ /$$| $$ | $$
/$$$$$$$/| $$$$$$$| $$ | $$$$$$$ | $$$$/| $$$$$$$
|_______/ \_______/|__/ \_______/ \___/ \____ $$
/$$ | $$
| $$$$$$/
by safetycli.com \______/
""" + SMALL_DIVIDER_SECTIONS
def __build_announcements_section(self, announcements: List[Dict]) -> List[str]:
"""
Build the announcements section of the report.
Args:
announcements (List[Dict]): List of announcement dictionaries.
Returns:
List[str]: Formatted announcements section.
"""
announcements_table = []
basic_announcements = get_basic_announcements(announcements)
if basic_announcements:
announcements_content = click.unstyle(build_announcements_section_content(basic_announcements,
columns=80))
announcements_table = [add_empty_line(), ' ANNOUNCEMENTS', add_empty_line(),
announcements_content, add_empty_line(), self.SMALL_DIVIDER_SECTIONS]
return announcements_table
def render_vulnerabilities(
self, announcements: List[Dict], vulnerabilities: List[Dict],
remediations: Dict[str, Any], full: bool, packages: List[Dict],
fixes: Tuple = ()
) -> str:
"""
Render the vulnerabilities section of the report.
Args:
announcements (List[Dict]): List of announcement dictionaries.
vulnerabilities (List[Dict]): List of vulnerability dictionaries.
remediations (Dict[str, Any]): Remediation data.
full (bool): Flag indicating full report.
packages (List[Dict]): List of package dictionaries.
fixes (Tuple, optional): Iterable of fixes.
Returns:
str: Rendered vulnerabilities report.
"""
primary_announcement = get_primary_announcement(announcements)
remediation_section = [click.unstyle(rem) for rem in build_remediation_section(remediations, columns=80)]
end_content = []
if primary_announcement:
end_content = [add_empty_line(),
build_primary_announcement(primary_announcement, columns=80, only_text=True),
self.SMALL_DIVIDER_SECTIONS]
announcement_section = self.__build_announcements_section(announcements)
ignored = {}
total_ignored = 0
unpinned_packages = defaultdict(list)
raw_vulns = []
for n, vuln in enumerate(vulnerabilities):
if vuln.ignored:
total_ignored += 1
ignored[vuln.package_name] = ignored.get(vuln.package_name, 0) + 1
if is_ignore_unpinned_mode(version=vuln.analyzed_version) and not full:
unpinned_packages[vuln.package_name].append(vuln)
continue
raw_vulns.append(vuln)
report_brief_section = click.unstyle(
build_report_brief_section(columns=80, primary_announcement=primary_announcement,
vulnerabilities_found=max(0, len(vulnerabilities)-total_ignored),
vulnerabilities_ignored=total_ignored,
remediations_recommended=remediations))
table = [self.TEXT_REPORT_BANNER] + announcement_section + [
report_brief_section,
'',
self.SMALL_DIVIDER_SECTIONS,
]
if vulnerabilities:
table += [" VULNERABILITIES FOUND", self.SMALL_DIVIDER_SECTIONS]
if unpinned_packages:
table.append('')
table.extend(map(click.unstyle, format_unpinned_vulnerabilities(unpinned_packages, columns=80)))
if not raw_vulns:
table.append('')
for vuln in raw_vulns:
table.append('\n' + format_vulnerability(vuln, full, only_text=True, columns=80))
final_brief = click.unstyle(get_final_brief(len(vulnerabilities), remediations, ignored, total_ignored,
kwargs={'columns': 80}))
table += [add_empty_line(), self.SMALL_DIVIDER_SECTIONS] + remediation_section + ['', final_brief, '', self.SMALL_DIVIDER_SECTIONS] + end_content
else:
table += [add_empty_line(), " No known security vulnerabilities found.", add_empty_line(),
self.SMALL_DIVIDER_SECTIONS] + end_content
return "\n".join(
table
)
def render_licenses(self, announcements: List[Dict], licenses: List[Dict]) -> str:
"""
Render the licenses section of the report.
Args:
announcements (List[Dict]): List of announcement dictionaries.
licenses (List[Dict]): List of license dictionaries.
Returns:
str: Rendered licenses report.
"""
unique_license_types = set([lic['license'] for lic in licenses])
report_brief_section = click.unstyle(
build_report_brief_section(columns=80, primary_announcement=get_primary_announcement(announcements),
licenses_found=len(unique_license_types)))
packages_licenses = licenses
announcements_table = self.__build_announcements_section(announcements)
final_brief = click.unstyle(
get_final_brief_license(unique_license_types, kwargs={'columns': 80}))
table = [self.TEXT_REPORT_BANNER] + announcements_table + [
report_brief_section,
self.SMALL_DIVIDER_SECTIONS,
" LICENSES",
self.SMALL_DIVIDER_SECTIONS,
add_empty_line(),
]
if not packages_licenses:
table.append(" No packages licenses found.")
table += [final_brief, add_empty_line(), self.SMALL_DIVIDER_SECTIONS]
return "\n".join(table)
for pkg_license in packages_licenses:
text = " {0}, version {1}, license {2}\n".format(pkg_license['package'], pkg_license['version'],
pkg_license['license'])
table.append(text)
table += [final_brief, add_empty_line(), self.SMALL_DIVIDER_SECTIONS]
return "\n".join(table)
def render_announcements(self, announcements: List[Dict]) -> str:
"""
Render the announcements section of the report.
Args:
announcements (List[Dict]): List of announcement dictionaries.
Returns:
str: Rendered announcements section.
"""
rows = self.__build_announcements_section(announcements)
rows.insert(0, self.SMALL_DIVIDER_SECTIONS)
return '\n'.join(rows)