Add source code files
This commit is contained in:
99
src/codeguard/utils/output.py
Normal file
99
src/codeguard/utils/output.py
Normal file
@@ -0,0 +1,99 @@
|
||||
"""Output formatting for CodeGuard."""
|
||||
|
||||
from typing import Any
|
||||
from codeguard.core.models import Finding, Severity
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
from rich.text import Text
|
||||
|
||||
|
||||
class OutputFormatter:
|
||||
SEVERITY_COLORS = {
|
||||
Severity.CRITICAL: "red",
|
||||
Severity.HIGH: "orange1",
|
||||
Severity.MEDIUM: "yellow",
|
||||
Severity.LOW: "green",
|
||||
Severity.INFO: "blue",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.console = Console()
|
||||
|
||||
@staticmethod
|
||||
def print(findings: list[Finding], output_format: str = "text") -> None:
|
||||
if output_format == "json":
|
||||
print(OutputFormatter.to_json(findings))
|
||||
else:
|
||||
OutputFormatter._print_text(findings)
|
||||
|
||||
@staticmethod
|
||||
def _print_text(findings: list[Finding]) -> None:
|
||||
if not findings:
|
||||
print("No security issues found.")
|
||||
return
|
||||
|
||||
console = Console()
|
||||
|
||||
table = Table(title="CodeGuard Security Findings")
|
||||
table.add_column("Severity", width=10)
|
||||
table.add_column("File", width=30)
|
||||
table.add_column("Line", width=6)
|
||||
table.add_column("Issue", width=40)
|
||||
table.add_column("Type", width=15)
|
||||
|
||||
for finding in findings:
|
||||
severity_text = Text(finding.severity.value.upper())
|
||||
severity_text.stylize(f"bold {OutputFormatter.SEVERITY_COLORS.get(finding.severity, 'white')}")
|
||||
|
||||
file_short = finding.location.file
|
||||
if len(file_short) > 28:
|
||||
file_short = "..." + file_short[-28:]
|
||||
|
||||
table.add_row(
|
||||
severity_text,
|
||||
file_short,
|
||||
str(finding.location.line),
|
||||
finding.title[:38],
|
||||
finding.type.value,
|
||||
)
|
||||
|
||||
console.print(table)
|
||||
|
||||
summary = OutputFormatter._get_summary(findings)
|
||||
console.print(f"\nSummary: {summary}")
|
||||
|
||||
@staticmethod
|
||||
def to_json(findings: list[Finding]) -> str:
|
||||
import json
|
||||
output = {
|
||||
"findings": [f.model_dump(mode="json") for f in findings],
|
||||
"summary": OutputFormatter._get_summary(findings),
|
||||
}
|
||||
return json.dumps(output, indent=2)
|
||||
|
||||
@staticmethod
|
||||
def to_yaml(findings: list[Finding]) -> str:
|
||||
import yaml
|
||||
output = {
|
||||
"findings": [f.model_dump(mode="json") for f in findings],
|
||||
"summary": OutputFormatter._get_summary(findings),
|
||||
}
|
||||
return yaml.dump(output)
|
||||
|
||||
@staticmethod
|
||||
def _get_summary(findings: list[Finding]) -> dict[str, Any]:
|
||||
severity_counts: dict[str, int] = {}
|
||||
type_counts: dict[str, int] = {}
|
||||
files_affected = set()
|
||||
|
||||
for f in findings:
|
||||
severity_counts[f.severity.value] = severity_counts.get(f.severity.value, 0) + 1
|
||||
type_counts[f.type.value] = type_counts.get(f.type.value, 0) + 1
|
||||
files_affected.add(f.location.file)
|
||||
|
||||
return {
|
||||
"total": len(findings),
|
||||
"by_severity": severity_counts,
|
||||
"by_type": type_counts,
|
||||
"files_affected": len(files_affected),
|
||||
}
|
||||
Reference in New Issue
Block a user