This commit is contained in:
92
src/depcheck/reporters/json.py
Normal file
92
src/depcheck/reporters/json.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
"""JSON reporter for CI/CD integration."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from depcheck.config import Config
|
||||||
|
from depcheck.models import ScanResult, Severity
|
||||||
|
|
||||||
|
|
||||||
|
class JSONReporter:
|
||||||
|
"""Report results in JSON format for CI/CD pipelines."""
|
||||||
|
|
||||||
|
def __init__(self, config: Optional[Config] = None):
|
||||||
|
self.config = config or Config()
|
||||||
|
|
||||||
|
def report(self, result: ScanResult) -> str:
|
||||||
|
"""Generate JSON report."""
|
||||||
|
report_data = self._build_report(result)
|
||||||
|
return json.dumps(report_data, indent=2)
|
||||||
|
|
||||||
|
def _build_report(self, result: ScanResult) -> dict[str, Any]:
|
||||||
|
"""Build report dictionary."""
|
||||||
|
highest_severity = result.get_highest_severity()
|
||||||
|
report: dict[str, Any] = {
|
||||||
|
"summary": {
|
||||||
|
"status": "success" if result.outdated_count == 0 and result.vulnerable_count == 0 else "issues_found",
|
||||||
|
"dependencies_scanned": len(result.dependencies),
|
||||||
|
"outdated_packages": result.outdated_count,
|
||||||
|
"vulnerabilities": result.vulnerable_count,
|
||||||
|
"highest_severity": highest_severity.value if highest_severity else None,
|
||||||
|
},
|
||||||
|
"dependencies": [],
|
||||||
|
"vulnerabilities": [],
|
||||||
|
"errors": result.scan_errors,
|
||||||
|
}
|
||||||
|
|
||||||
|
for dep in result.dependencies:
|
||||||
|
dep_data = {
|
||||||
|
"name": dep.name,
|
||||||
|
"current_version": dep.current_version,
|
||||||
|
"latest_version": dep.latest_version,
|
||||||
|
"is_outdated": dep.is_outdated,
|
||||||
|
"package_manager": dep.package_manager.value,
|
||||||
|
"category": dep.category,
|
||||||
|
"source_file": dep.source_file,
|
||||||
|
}
|
||||||
|
report["dependencies"].append(dep_data)
|
||||||
|
|
||||||
|
for dep, vuln in result.vulnerabilities:
|
||||||
|
vuln_data = {
|
||||||
|
"package": dep.name,
|
||||||
|
"current_version": dep.current_version,
|
||||||
|
"cve_id": vuln.cve_id,
|
||||||
|
"severity": vuln.severity.value,
|
||||||
|
"description": vuln.description,
|
||||||
|
"affected_versions": vuln.affected_versions,
|
||||||
|
"fixed_version": vuln.fixed_version,
|
||||||
|
"references": vuln.references,
|
||||||
|
}
|
||||||
|
report["vulnerabilities"].append(vuln_data)
|
||||||
|
|
||||||
|
return report
|
||||||
|
|
||||||
|
def get_exit_code(self, result: ScanResult) -> int:
|
||||||
|
"""Determine exit code based on results."""
|
||||||
|
if result.scan_errors:
|
||||||
|
return 2
|
||||||
|
|
||||||
|
highest_severity = result.get_highest_severity()
|
||||||
|
if not highest_severity:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
severity_order = [Severity.CRITICAL, Severity.HIGH, Severity.MEDIUM, Severity.LOW]
|
||||||
|
threshold = self.config.fail_level
|
||||||
|
|
||||||
|
try:
|
||||||
|
threshold_index = severity_order.index(threshold)
|
||||||
|
except ValueError:
|
||||||
|
threshold_index = 2
|
||||||
|
|
||||||
|
if highest_severity in severity_order[:threshold_index + 1]:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def print_error(self, message: str) -> None:
|
||||||
|
"""Print error message (for CLI output)."""
|
||||||
|
print(f"ERROR: {message}")
|
||||||
|
|
||||||
|
def print_warning(self, message: str) -> None:
|
||||||
|
"""Print warning message (for CLI output)."""
|
||||||
|
print(f"WARNING: {message}")
|
||||||
Reference in New Issue
Block a user