// Package main implements the audit-verifier standalone verification tool package main import ( "flag" "fmt" "log/slog" "os" "time" "github.com/jfraeys/fetch_ml/internal/audit" "github.com/jfraeys/fetch_ml/internal/logging" ) func main() { var ( logPath string interval time.Duration continuous bool verbose bool ) flag.StringVar(&logPath, "log-path", "", "Path to audit log file to verify (required)") flag.DurationVar(&interval, "interval", 15*time.Minute, "Verification interval for continuous mode") flag.BoolVar(&continuous, "continuous", false, "Run continuous verification in a loop") flag.BoolVar(&verbose, "verbose", false, "Enable verbose output") flag.Parse() if logPath == "" { fmt.Fprintln(os.Stderr, "Error: -log-path is required") flag.Usage() os.Exit(1) } // Setup logging logLevel := slog.LevelInfo if verbose { logLevel = slog.LevelDebug } logger := logging.NewLogger(logLevel, false) verifier := audit.NewChainVerifier(logger) if continuous { fmt.Printf("Starting continuous audit verification every %v...\n", interval) fmt.Printf("Press Ctrl+C to stop\n\n") // Run with alert function that prints to stdout verifier.ContinuousVerification(logPath, interval, func(result *audit.VerificationResult) { printResult(result) if !result.Valid { // In continuous mode, we don't exit on tampering - we keep monitoring // The alert function should notify appropriate channels (email, slack, etc.) fmt.Println("\n*** TAMPERING DETECTED - INVESTIGATE IMMEDIATELY ***") } }) } else { // Single verification run fmt.Printf("Verifying audit log: %s\n", logPath) result, err := verifier.VerifyLogFile(logPath) if err != nil { fmt.Fprintf(os.Stderr, "Verification failed: %v\n", err) os.Exit(1) } printResult(result) if !result.Valid { fmt.Println("\n*** VERIFICATION FAILED - AUDIT CHAIN TAMPERING DETECTED ***") os.Exit(2) } fmt.Println("\n✓ Audit chain integrity verified") } } func printResult(result *audit.VerificationResult) { fmt.Printf("\nVerification Time: %s\n", result.Timestamp.Format(time.RFC3339)) fmt.Printf("Total Events: %d\n", result.TotalEvents) fmt.Printf("Valid: %v\n", result.Valid) if result.ChainRootHash != "" { fmt.Printf("Chain Root Hash: %s...\n", result.ChainRootHash[:16]) } if !result.Valid { if result.FirstTampered != -1 { fmt.Printf("First Tampered Event: %d\n", result.FirstTampered) } if result.Error != "" { fmt.Printf("Error: %s\n", result.Error) } } }