from __future__ import annotations from rich.console import Console from rich.table import Table from rich.text import Text from vuln_scan.core.models import Finding from vuln_scan.reporting.base import Reporter BOLD_RED = "bold red" def _sev_style(sev: str) -> str: return { "critical": BOLD_RED, "high": "magenta", "moderate": "yellow", "low": "cyan", }.get(sev.lower(), "") def _block_style(blocking: bool) -> str: return BOLD_RED if blocking else "green" def _shorten_path(p: str | None, max_len: int = 40) -> str: if not p: return "-" if len(p) <= max_len: return p return "…" + p[-max_len:] class RichReporter(Reporter): def render(self, findings: list[Finding], dependabot_url: str) -> str: if not findings: console = Console(record=True) console.print("[bold green]✓ No Vulnerabilities Found[/bold green]") return console.export_text().strip() rows = [self.extract_row(f) for f in findings] blocking_count, non_blocking_count = self.summary_counts(findings) table = Table( title="Dependency Vulnerabilities", header_style="bold white", show_lines=False, ) table.add_column("File", overflow="fold", max_width=40) table.add_column("Package") table.add_column("Current Version") table.add_column("Min. Required Version") table.add_column("Vulnerable Range") table.add_column("Severity") table.add_column("Blocking") table.add_column("Grace Period") table.add_column("Blocking Date") table.add_column("Reason") for row in rows: severity_text = Text( row.severity.upper(), style=_sev_style(row.severity), ) blocking_text = Text( "YES" if row.is_blocking else "NO", style=_block_style(row.is_blocking), ) table.add_row( _shorten_path(row.file), row.package, row.version, row.patch, row.vuln_range, severity_text, blocking_text, row.grace_period, row.blocking_date, row.reason, ) console = Console(record=True) console.print(table) console.print( f"[bold]Total findings:[/bold] {len(findings)} " f"([bold red]{blocking_count} blocking[/bold red], " f"[green]{non_blocking_count} non-blocking[/green])" ) console.print(f"[dim]Dependabot alerts:[/dim] {dependabot_url}") return console.export_text().strip()