// Package audit provides tamper-evident audit logging with hash chaining package audit import ( "context" "fmt" "time" ) // TamperAlert represents a tampering detection event type TamperAlert struct { DetectedAt time.Time `json:"detected_at"` Severity string `json:"severity"` // "critical", "warning" Description string `json:"description"` ExpectedHash string `json:"expected_hash,omitempty"` ActualHash string `json:"actual_hash,omitempty"` FilePath string `json:"file_path,omitempty"` } // AlertManager defines the interface for tamper alerting type AlertManager interface { Alert(ctx context.Context, a TamperAlert) error } // LoggingAlerter logs alerts to a standard logger type LoggingAlerter struct { logger interface { Error(msg string, keysAndValues ...any) Warn(msg string, keysAndValues ...any) } } // NewLoggingAlerter creates a new logging alerter func NewLoggingAlerter(logger interface { Error(msg string, keysAndValues ...any) Warn(msg string, keysAndValues ...any) }) *LoggingAlerter { return &LoggingAlerter{logger: logger} } // Alert logs the tamper alert func (l *LoggingAlerter) Alert(_ context.Context, a TamperAlert) error { if l.logger == nil { return nil } if a.Severity == "critical" { l.logger.Error("TAMPERING DETECTED", "description", a.Description, "expected_hash", a.ExpectedHash, "actual_hash", a.ActualHash, "file_path", a.FilePath, "detected_at", a.DetectedAt, ) } else { l.logger.Warn("Potential tampering detected", "description", a.Description, "expected_hash", a.ExpectedHash, "actual_hash", a.ActualHash, "file_path", a.FilePath, "detected_at", a.DetectedAt, ) } return nil } // MultiAlerter sends alerts to multiple backends type MultiAlerter struct { alerters []AlertManager } // NewMultiAlerter creates a new multi-alerter func NewMultiAlerter(alerters ...AlertManager) *MultiAlerter { return &MultiAlerter{alerters: alerters} } // Alert sends alert to all configured alerters func (m *MultiAlerter) Alert(ctx context.Context, a TamperAlert) error { var errs []error for _, alerter := range m.alerters { if err := alerter.Alert(ctx, a); err != nil { errs = append(errs, err) } } if len(errs) > 0 { return fmt.Errorf("alert failures: %v", errs) } return nil }