From a84ce8a4502d7fa522224500da2f8d5d20ea5255 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Mon, 2 Feb 2026 21:35:56 +0000 Subject: [PATCH] Add output formatters --- depaudit/output/table_formatter.py | 159 +++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 depaudit/output/table_formatter.py diff --git a/depaudit/output/table_formatter.py b/depaudit/output/table_formatter.py new file mode 100644 index 0000000..a52f018 --- /dev/null +++ b/depaudit/output/table_formatter.py @@ -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