package report import ( "encoding/json" "fmt" "os" "os/exec" "strings" "github.com/user/package-review/internal/security" ) // WriteJSON writes the audit results as JSON func WriteJSON(audit *security.Audit, outputPath string) error { data := map[string]interface{}{ "timestamp": audit.Inventory.Timestamp, "system": audit.Inventory.System, "summary": map[string]interface{}{ "total_packages": audit.Inventory.CountPackages(), "by_source": audit.Inventory.CountBySource(), "total_findings": len(audit.Findings), "vulnerable_count": audit.CountVulnerable(), "high_severity": len(audit.FilterByMinSeverity(7.0)), "critical_findings": len(audit.FilterByMinSeverity(9.0)), }, "findings": audit.Findings, "mitigations": audit.Mitigations, } jsonBytes, err := json.MarshalIndent(data, "", " ") if err != nil { return fmt.Errorf("marshal JSON: %w", err) } if err := os.WriteFile(outputPath, jsonBytes, 0o644); err != nil { return fmt.Errorf("write JSON file: %w", err) } return nil } // GenerateAstroSite generates an Astro static site from the audit results func GenerateAstroSite(audit *security.Audit, outputHTMLPath string) error { // Create intermediate JSON for Astro to consume astroDataFile := "audit_data.json" // Write audit data to JSON file that Astro will use data := map[string]interface{}{ "timestamp": audit.Inventory.Timestamp, "system": audit.Inventory.System, "findings": audit.Findings, "mitigations": audit.Mitigations, "summary": map[string]interface{}{ "total_packages": audit.Inventory.CountPackages(), "vulnerable_count": audit.CountVulnerable(), "high_severity": len(audit.FilterByMinSeverity(7.0)), "critical_findings": len(audit.FilterByMinSeverity(9.0)), }, } jsonBytes, err := json.MarshalIndent(data, "", " ") if err != nil { return fmt.Errorf("marshal Astro data: %w", err) } if err := os.WriteFile(astroDataFile, jsonBytes, 0o644); err != nil { return fmt.Errorf("write Astro data file: %w", err) } // Check if Astro is available _, err = exec.LookPath("astro") if err != nil { // Fallback: generate a simple HTML report without Astro fmt.Println(" ⚠️ Astro not found, generating basic HTML report...") return generateBasicHTML(audit, outputHTMLPath) } // Build Astro site cmd := exec.Command("astro", "build", "--outDir", "dist") if err := cmd.Run(); err != nil { fmt.Println(" ⚠️ Astro build failed, generating basic HTML report...") return generateBasicHTML(audit, outputHTMLPath) } return nil } // generateBasicHTML creates a simple HTML report as fallback func generateBasicHTML(audit *security.Audit, outputPath string) error { html := `
Generated: ` + audit.Inventory.Timestamp.String() + `
` + finding.Summary + `
` // Find mitigation for this finding for _, mit := range audit.Mitigations { if mit.Finding.CVE == finding.CVE && mit.Finding.PackageName == finding.PackageName { html += `