// Package report - Output formatters package report import ( "encoding/json" "fmt" "io" "strings" "text/template" "time" ) // Formatter formats reports for output type Formatter interface { Format(report *Report, w io.Writer) error } // JSONFormatter formats reports as JSON type JSONFormatter struct { Indent bool } // Format writes the report as JSON func (f *JSONFormatter) Format(report *Report, w io.Writer) error { var data []byte var err error if f.Indent { data, err = json.MarshalIndent(report, "", " ") } else { data, err = json.Marshal(report) } if err != nil { return err } _, err = w.Write(data) return err } // MarkdownFormatter formats reports as Markdown type MarkdownFormatter struct{} // Format writes the report as Markdown func (f *MarkdownFormatter) Format(report *Report, w io.Writer) error { tmpl := template.Must(template.New("report").Funcs(template.FuncMap{ "statusIcon": StatusIcon, "severityIcon": SeverityIcon, "formatTime": func(t time.Time) string { return t.Format("2006-01-02 15:04:05") }, "formatDate": func(t time.Time) string { return t.Format("2006-01-02") }, "upper": strings.ToUpper, }).Parse(markdownTemplate)) return tmpl.Execute(w, report) } const markdownTemplate = `# {{.Title}} **Generated:** {{formatTime .GeneratedAt}} **Period:** {{formatDate .PeriodStart}} to {{formatDate .PeriodEnd}} **Overall Status:** {{statusIcon .Status}} {{.Status}} **Compliance Score:** {{printf "%.1f" .Score}}% --- ## Executive Summary {{.Description}} | Metric | Value | |--------|-------| | Total Controls | {{.Summary.TotalControls}} | | Compliant | {{.Summary.CompliantControls}} | | Non-Compliant | {{.Summary.NonCompliantControls}} | | Partial | {{.Summary.PartialControls}} | | Compliance Rate | {{printf "%.1f" .Summary.ComplianceRate}}% | | Open Findings | {{.Summary.OpenFindings}} | | Risk Score | {{printf "%.1f" .Summary.RiskScore}} | --- ## Compliance Categories {{range .Categories}} ### {{statusIcon .Status}} {{.Name}} **Status:** {{.Status}} | **Score:** {{printf "%.1f" .Score}}% {{.Description}} | Control | Reference | Status | Notes | |---------|-----------|--------|-------| {{range .Controls}}| {{.Name}} | {{.Reference}} | {{statusIcon .Status}} | {{.Notes}} | {{end}} {{end}} --- ## Findings {{if .Findings}} | ID | Severity | Title | Status | |----|----------|-------|--------| {{range .Findings}}| {{.ID}} | {{severityIcon .Severity}} {{.Severity}} | {{.Title}} | {{.Status}} | {{end}} ### Finding Details {{range .Findings}} #### {{severityIcon .Severity}} {{.Title}} - **ID:** {{.ID}} - **Control:** {{.ControlID}} - **Severity:** {{.Severity}} - **Type:** {{.Type}} - **Status:** {{.Status}} - **Detected:** {{formatTime .DetectedAt}} **Description:** {{.Description}} **Impact:** {{.Impact}} **Recommendation:** {{.Recommendation}} --- {{end}} {{else}} No open findings. {{end}} --- ## Evidence Summary {{if .Evidence}} | ID | Type | Description | Collected | |----|------|-------------|-----------| {{range .Evidence}}| {{.ID}} | {{.Type}} | {{.Description}} | {{formatTime .CollectedAt}} | {{end}} {{else}} No evidence collected. {{end}} --- *Report generated by dbbackup compliance module* ` // HTMLFormatter formats reports as HTML type HTMLFormatter struct{} // Format writes the report as HTML func (f *HTMLFormatter) Format(report *Report, w io.Writer) error { tmpl := template.Must(template.New("report").Funcs(template.FuncMap{ "statusIcon": StatusIcon, "statusClass": statusClass, "severityIcon": SeverityIcon, "severityClass": severityClass, "formatTime": func(t time.Time) string { return t.Format("2006-01-02 15:04:05") }, "formatDate": func(t time.Time) string { return t.Format("2006-01-02") }, }).Parse(htmlTemplate)) return tmpl.Execute(w, report) } func statusClass(s ComplianceStatus) string { switch s { case StatusCompliant: return "status-compliant" case StatusNonCompliant: return "status-noncompliant" case StatusPartial: return "status-partial" default: return "status-unknown" } } func severityClass(s FindingSeverity) string { switch s { case SeverityCritical: return "severity-critical" case SeverityHigh: return "severity-high" case SeverityMedium: return "severity-medium" case SeverityLow: return "severity-low" default: return "severity-unknown" } } const htmlTemplate = `
{{.Description}}
{{.Description}}
| Control | Reference | Status | Notes |
|---|---|---|---|
| {{.Name}} | {{.Reference}} | {{statusIcon .Status}} | {{.Notes}} |
Description: {{.Description}}
Impact: {{.Impact}}
Recommendation: {{.Recommendation}}
| ID | Type | Description | Collected |
|---|---|---|---|
| {{.ID}} | {{.Type}} | {{.Description}} | {{formatTime .CollectedAt}} |