Files
dbbackup/internal/report/report.go
Alexander Renz 3e41d88445
All checks were successful
CI/CD / Test (push) Successful in 1m13s
CI/CD / Lint (push) Successful in 1m20s
CI/CD / Build & Release (push) Successful in 3m10s
v3.42.11: Replace all Unicode emojis with ASCII text
- Replace all emoji characters with ASCII equivalents throughout codebase
- Replace Unicode box-drawing characters (═║╔╗╚╝━─) with ASCII (+|-=)
- Replace checkmarks (✓✗) with [OK]/[FAIL] markers
- 59 files updated, 741 lines changed
- Improves terminal compatibility and reduces visual noise
2026-01-08 09:42:01 +01:00

326 lines
9.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package report provides compliance report generation
package report
import (
"encoding/json"
"fmt"
"time"
)
// ReportType represents the compliance framework type
type ReportType string
const (
ReportSOC2 ReportType = "soc2"
ReportGDPR ReportType = "gdpr"
ReportHIPAA ReportType = "hipaa"
ReportPCIDSS ReportType = "pci-dss"
ReportISO27001 ReportType = "iso27001"
ReportCustom ReportType = "custom"
)
// ComplianceStatus represents the status of a compliance check
type ComplianceStatus string
const (
StatusCompliant ComplianceStatus = "compliant"
StatusNonCompliant ComplianceStatus = "non_compliant"
StatusPartial ComplianceStatus = "partial"
StatusNotApplicable ComplianceStatus = "not_applicable"
StatusUnknown ComplianceStatus = "unknown"
)
// Report represents a compliance report
type Report struct {
ID string `json:"id"`
Type ReportType `json:"type"`
Title string `json:"title"`
Description string `json:"description"`
GeneratedAt time.Time `json:"generated_at"`
GeneratedBy string `json:"generated_by"`
PeriodStart time.Time `json:"period_start"`
PeriodEnd time.Time `json:"period_end"`
Status ComplianceStatus `json:"overall_status"`
Score float64 `json:"score"` // 0-100
Categories []Category `json:"categories"`
Summary Summary `json:"summary"`
Findings []Finding `json:"findings"`
Evidence []Evidence `json:"evidence"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// Category represents a compliance category
type Category struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Status ComplianceStatus `json:"status"`
Score float64 `json:"score"`
Weight float64 `json:"weight"`
Controls []Control `json:"controls"`
}
// Control represents a compliance control
type Control struct {
ID string `json:"id"`
Reference string `json:"reference"` // e.g., "SOC2 CC6.1"
Name string `json:"name"`
Description string `json:"description"`
Status ComplianceStatus `json:"status"`
Evidence []string `json:"evidence_ids,omitempty"`
Findings []string `json:"finding_ids,omitempty"`
LastChecked time.Time `json:"last_checked"`
Notes string `json:"notes,omitempty"`
}
// Finding represents a compliance finding
type Finding struct {
ID string `json:"id"`
ControlID string `json:"control_id"`
Type FindingType `json:"type"`
Severity FindingSeverity `json:"severity"`
Title string `json:"title"`
Description string `json:"description"`
Impact string `json:"impact"`
Recommendation string `json:"recommendation"`
Status FindingStatus `json:"status"`
DetectedAt time.Time `json:"detected_at"`
ResolvedAt *time.Time `json:"resolved_at,omitempty"`
Evidence []string `json:"evidence_ids,omitempty"`
}
// FindingType represents the type of finding
type FindingType string
const (
FindingGap FindingType = "gap"
FindingViolation FindingType = "violation"
FindingObservation FindingType = "observation"
FindingRecommendation FindingType = "recommendation"
)
// FindingSeverity represents finding severity
type FindingSeverity string
const (
SeverityLow FindingSeverity = "low"
SeverityMedium FindingSeverity = "medium"
SeverityHigh FindingSeverity = "high"
SeverityCritical FindingSeverity = "critical"
)
// FindingStatus represents finding status
type FindingStatus string
const (
FindingOpen FindingStatus = "open"
FindingAccepted FindingStatus = "accepted"
FindingResolved FindingStatus = "resolved"
)
// Evidence represents compliance evidence
type Evidence struct {
ID string `json:"id"`
Type EvidenceType `json:"type"`
Description string `json:"description"`
Source string `json:"source"`
CollectedAt time.Time `json:"collected_at"`
Hash string `json:"hash,omitempty"`
Data interface{} `json:"data,omitempty"`
}
// EvidenceType represents the type of evidence
type EvidenceType string
const (
EvidenceBackupLog EvidenceType = "backup_log"
EvidenceRestoreLog EvidenceType = "restore_log"
EvidenceDrillResult EvidenceType = "drill_result"
EvidenceEncryptionProof EvidenceType = "encryption_proof"
EvidenceRetentionProof EvidenceType = "retention_proof"
EvidenceAccessLog EvidenceType = "access_log"
EvidenceAuditLog EvidenceType = "audit_log"
EvidenceConfiguration EvidenceType = "configuration"
EvidenceScreenshot EvidenceType = "screenshot"
EvidenceOther EvidenceType = "other"
)
// Summary provides a high-level overview
type Summary struct {
TotalControls int `json:"total_controls"`
CompliantControls int `json:"compliant_controls"`
NonCompliantControls int `json:"non_compliant_controls"`
PartialControls int `json:"partial_controls"`
NotApplicable int `json:"not_applicable"`
OpenFindings int `json:"open_findings"`
CriticalFindings int `json:"critical_findings"`
HighFindings int `json:"high_findings"`
MediumFindings int `json:"medium_findings"`
LowFindings int `json:"low_findings"`
ComplianceRate float64 `json:"compliance_rate"`
RiskScore float64 `json:"risk_score"`
}
// ReportConfig configures report generation
type ReportConfig struct {
Type ReportType `json:"type"`
Title string `json:"title"`
Description string `json:"description"`
PeriodStart time.Time `json:"period_start"`
PeriodEnd time.Time `json:"period_end"`
IncludeDatabases []string `json:"include_databases,omitempty"`
ExcludeDatabases []string `json:"exclude_databases,omitempty"`
CatalogPath string `json:"catalog_path"`
OutputFormat OutputFormat `json:"output_format"`
OutputPath string `json:"output_path"`
IncludeEvidence bool `json:"include_evidence"`
CustomControls []Control `json:"custom_controls,omitempty"`
}
// OutputFormat represents report output format
type OutputFormat string
const (
FormatJSON OutputFormat = "json"
FormatHTML OutputFormat = "html"
FormatPDF OutputFormat = "pdf"
FormatMarkdown OutputFormat = "markdown"
)
// NewReport creates a new report
func NewReport(reportType ReportType, title string) *Report {
return &Report{
ID: generateID(),
Type: reportType,
Title: title,
GeneratedAt: time.Now(),
Categories: make([]Category, 0),
Findings: make([]Finding, 0),
Evidence: make([]Evidence, 0),
Metadata: make(map[string]string),
}
}
// AddCategory adds a category to the report
func (r *Report) AddCategory(cat Category) {
r.Categories = append(r.Categories, cat)
}
// AddFinding adds a finding to the report
func (r *Report) AddFinding(f Finding) {
r.Findings = append(r.Findings, f)
}
// AddEvidence adds evidence to the report
func (r *Report) AddEvidence(e Evidence) {
r.Evidence = append(r.Evidence, e)
}
// Calculate computes the summary and overall status
func (r *Report) Calculate() {
var totalWeight float64
var weightedScore float64
for _, cat := range r.Categories {
totalWeight += cat.Weight
weightedScore += cat.Score * cat.Weight
for _, ctrl := range cat.Controls {
r.Summary.TotalControls++
switch ctrl.Status {
case StatusCompliant:
r.Summary.CompliantControls++
case StatusNonCompliant:
r.Summary.NonCompliantControls++
case StatusPartial:
r.Summary.PartialControls++
case StatusNotApplicable:
r.Summary.NotApplicable++
}
}
}
for _, f := range r.Findings {
if f.Status == FindingOpen {
r.Summary.OpenFindings++
switch f.Severity {
case SeverityCritical:
r.Summary.CriticalFindings++
case SeverityHigh:
r.Summary.HighFindings++
case SeverityMedium:
r.Summary.MediumFindings++
case SeverityLow:
r.Summary.LowFindings++
}
}
}
if totalWeight > 0 {
r.Score = weightedScore / totalWeight
}
applicable := r.Summary.TotalControls - r.Summary.NotApplicable
if applicable > 0 {
r.Summary.ComplianceRate = float64(r.Summary.CompliantControls) / float64(applicable) * 100
}
// Calculate risk score based on findings
r.Summary.RiskScore = float64(r.Summary.CriticalFindings)*10 +
float64(r.Summary.HighFindings)*5 +
float64(r.Summary.MediumFindings)*2 +
float64(r.Summary.LowFindings)*1
// Determine overall status
if r.Summary.NonCompliantControls == 0 && r.Summary.CriticalFindings == 0 {
if r.Summary.PartialControls == 0 {
r.Status = StatusCompliant
} else {
r.Status = StatusPartial
}
} else {
r.Status = StatusNonCompliant
}
}
// ToJSON converts the report to JSON
func (r *Report) ToJSON() ([]byte, error) {
return json.MarshalIndent(r, "", " ")
}
func generateID() string {
return fmt.Sprintf("RPT-%d", time.Now().UnixNano())
}
// StatusIcon returns an icon for a compliance status
func StatusIcon(s ComplianceStatus) string {
switch s {
case StatusCompliant:
return "[OK]"
case StatusNonCompliant:
return "[FAIL]"
case StatusPartial:
return "[WARN]"
case StatusNotApplicable:
return ""
default:
return "❓"
}
}
// SeverityIcon returns an icon for a finding severity
func SeverityIcon(s FindingSeverity) string {
switch s {
case SeverityCritical:
return "🔴"
case SeverityHigh:
return "🟠"
case SeverityMedium:
return "🟡"
case SeverityLow:
return "🟢"
default:
return "⚪"
}
}