Add output formatters
This commit is contained in:
159
depaudit/output/table_formatter.py
Normal file
159
depaudit/output/table_formatter.py
Normal file
@@ -0,0 +1,159 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
from rich.text import Text
|
||||
from rich import box as rich_box
|
||||
|
||||
from depaudit.output import AuditResult, Formatter
|
||||
|
||||
SEVERITY_COLORS = {
|
||||
"critical": "red",
|
||||
"high": "orange3",
|
||||
"medium": "yellow",
|
||||
"low": "blue",
|
||||
"unknown": "grey",
|
||||
}
|
||||
|
||||
|
||||
class TableFormatter(Formatter):
|
||||
def __init__(self, use_color: bool = True, verbosity: str = "info"):
|
||||
super().__init__(use_color, verbosity)
|
||||
self.console = Console(force_terminal=use_color, color_system="auto" if use_color else None)
|
||||
|
||||
def format(self, result: AuditResult) -> str:
|
||||
lines = []
|
||||
|
||||
summary = result.get_summary()
|
||||
lines.append("")
|
||||
lines.append("=" * 60)
|
||||
lines.append("DepAudit Report")
|
||||
lines.append("=" * 60)
|
||||
lines.append(f"Scanned Files: {result.scanned_count}")
|
||||
lines.append(f"Scan Duration: {result.scan_duration:.2f}s")
|
||||
lines.append("")
|
||||
|
||||
if result.vulnerabilities:
|
||||
lines.append("[VULNERABILITIES]")
|
||||
vuln_table = self._create_vulnerability_table(result.vulnerabilities)
|
||||
lines.append(self._render_table(vuln_table))
|
||||
|
||||
if result.outdated:
|
||||
lines.append("[OUTDATED PACKAGES]")
|
||||
outdated_table = self._create_outdated_table(result.outdated)
|
||||
lines.append(self._render_table(outdated_table))
|
||||
|
||||
if result.license_issues:
|
||||
lines.append("[LICENSE ISSUES]")
|
||||
license_table = self._create_license_table(result.license_issues)
|
||||
lines.append(self._render_table(license_table))
|
||||
|
||||
if result.unused:
|
||||
lines.append("[UNUSED DEPENDENCIES]")
|
||||
unused_table = self._create_unused_table(result.unused)
|
||||
lines.append(self._render_table(unused_table))
|
||||
|
||||
if summary["total_vulnerabilities"] > 0:
|
||||
lines.append("")
|
||||
lines.append("SEVERITY SUMMARY:")
|
||||
for severity, count in summary["severity_breakdown"].items():
|
||||
if count > 0:
|
||||
color = SEVERITY_COLORS.get(severity, "grey")
|
||||
lines.append(f" {severity.upper()}: {count}")
|
||||
|
||||
lines.append("")
|
||||
lines.append("=" * 60)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def _render_table(self, table: Table) -> str:
|
||||
from io import StringIO
|
||||
output = StringIO()
|
||||
self.console.begin_capture()
|
||||
self.console.print(table)
|
||||
captured = self.console.end_capture()
|
||||
return captured
|
||||
|
||||
def _create_vulnerability_table(self, vulnerabilities: list[dict]) -> Table:
|
||||
table = Table(box=rich_box.SIMPLE, show_header=True, header_style="bold")
|
||||
table.add_column("Severity", width=10)
|
||||
table.add_column("Package", width=25)
|
||||
table.add_column("Current", width=12)
|
||||
table.add_column("Fixed", width=12)
|
||||
table.add_column("ID", width=20)
|
||||
|
||||
for vuln in vulnerabilities:
|
||||
severity = vuln.get("severity", "unknown")
|
||||
color = SEVERITY_COLORS.get(severity, "grey")
|
||||
table.add_row(
|
||||
Text(severity.upper(), style=color),
|
||||
vuln.get("package_name", ""),
|
||||
vuln.get("current_version", ""),
|
||||
vuln.get("fixed_version", "") or "N/A",
|
||||
vuln.get("id", ""),
|
||||
)
|
||||
|
||||
return table
|
||||
|
||||
def _create_outdated_table(self, outdated: list[dict]) -> Table:
|
||||
table = Table(box=rich_box.SIMPLE, show_header=True, header_style="bold")
|
||||
table.add_column("Package", width=30)
|
||||
table.add_column("Current", width=15)
|
||||
table.add_column("Latest", width=15)
|
||||
table.add_column("Update Type", width=15)
|
||||
|
||||
for pkg in outdated:
|
||||
update_type = []
|
||||
if pkg.get("major_available"):
|
||||
update_type.append("major")
|
||||
if pkg.get("minor_available"):
|
||||
update_type.append("minor")
|
||||
if pkg.get("patch_available"):
|
||||
update_type.append("patch")
|
||||
|
||||
table.add_row(
|
||||
pkg.get("package_name", ""),
|
||||
pkg.get("current_version", ""),
|
||||
pkg.get("latest_version", ""),
|
||||
", ".join(update_type) if update_type else "latest",
|
||||
)
|
||||
|
||||
return table
|
||||
|
||||
def _create_license_table(self, issues: list[dict]) -> Table:
|
||||
table = Table(box=rich_box.SIMPLE, show_header=True, header_style="bold")
|
||||
table.add_column("Package", width=30)
|
||||
table.add_column("License", width=20)
|
||||
table.add_column("Status", width=15)
|
||||
|
||||
for issue in issues:
|
||||
status = issue.get("status", "unknown")
|
||||
if status == "blocked":
|
||||
style = "red"
|
||||
elif status == "allowed":
|
||||
style = "green"
|
||||
else:
|
||||
style = "yellow"
|
||||
|
||||
table.add_row(
|
||||
issue.get("package_name", ""),
|
||||
issue.get("license", "") or "Unknown",
|
||||
Text(status, style=style),
|
||||
)
|
||||
|
||||
return table
|
||||
|
||||
def _create_unused_table(self, unused: list[dict]) -> Table:
|
||||
table = Table(box=rich_box.SIMPLE, show_header=True, header_style="bold")
|
||||
table.add_column("Package", width=30)
|
||||
table.add_column("Version", width=15)
|
||||
table.add_column("Reason", width=40)
|
||||
|
||||
for item in unused:
|
||||
table.add_row(
|
||||
item.get("package_name", ""),
|
||||
item.get("version", ""),
|
||||
item.get("reason", ""),
|
||||
)
|
||||
|
||||
return table
|
||||
Reference in New Issue
Block a user