package security import ( "bufio" "encoding/json" "fmt" "io" "os" "regexp" "sort" "strings" "time" "github.com/fraktal/mev-beta/internal/logger" ) // AuditAnalyzer provides advanced analysis of security audit logs type AuditAnalyzer struct { logger *logger.Logger config *AnalyzerConfig patterns map[string]*regexp.Regexp investigations []*Investigation reports []*AnalysisReport } // AnalyzerConfig configures the audit log analyzer type AnalyzerConfig struct { // File paths AuditLogPaths []string `json:"audit_log_paths"` OutputDirectory string `json:"output_directory"` ArchiveDirectory string `json:"archive_directory"` // Analysis settings TimeWindow time.Duration `json:"time_window"` // Analysis time window SuspiciousThreshold float64 `json:"suspicious_threshold"` // Threshold for suspicious activity AlertThreshold int `json:"alert_threshold"` // Number of events to trigger alert MaxLogSize int64 `json:"max_log_size"` // Max log file size to process // Pattern detection EnablePatternDetection bool `json:"enable_pattern_detection"` CustomPatterns []string `json:"custom_patterns"` IgnorePatterns []string `json:"ignore_patterns"` // Report settings GenerateReports bool `json:"generate_reports"` ReportFormats []string `json:"report_formats"` // json, csv, html, pdf ReportSchedule time.Duration `json:"report_schedule"` RetentionPeriod time.Duration `json:"retention_period"` // Investigation settings AutoInvestigate bool `json:"auto_investigate"` InvestigationDepth int `json:"investigation_depth"` // 1-5 depth levels } // Investigation represents a security investigation case type Investigation struct { ID string `json:"id"` Title string `json:"title"` Description string `json:"description"` Severity InvestigationSeverity `json:"severity"` Status InvestigationStatus `json:"status"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` AssignedTo string `json:"assigned_to"` RelatedEvents []string `json:"related_events"` Findings []*Finding `json:"findings"` Timeline []*TimelineEvent `json:"timeline"` Evidence []*Evidence `json:"evidence"` Recommendations []*Recommendation `json:"recommendations"` Metadata map[string]interface{} `json:"metadata"` } // InvestigationSeverity represents investigation severity levels (using existing EventSeverity) type InvestigationSeverity = EventSeverity // InvestigationStatus represents investigation status type InvestigationStatus string const ( StatusOpen InvestigationStatus = "OPEN" StatusInProgress InvestigationStatus = "IN_PROGRESS" StatusResolved InvestigationStatus = "RESOLVED" StatusClosed InvestigationStatus = "CLOSED" StatusEscalated InvestigationStatus = "ESCALATED" ) // Finding represents a security finding type Finding struct { ID string `json:"id"` Type FindingType `json:"type"` Severity FindingSeverity `json:"severity"` Title string `json:"title"` Description string `json:"description"` Evidence []string `json:"evidence"` MITRE []string `json:"mitre_tactics"` // MITRE ATT&CK tactics CVE []string `json:"cve_references"` Risk RiskAssessment `json:"risk_assessment"` Remediation RemediationGuidance `json:"remediation"` CreatedAt time.Time `json:"created_at"` Metadata map[string]interface{} `json:"metadata"` } // FindingType represents types of security findings type FindingType string const ( FindingTypeVulnerability FindingType = "VULNERABILITY" FindingTypeMisconfiguration FindingType = "MISCONFIGURATION" FindingTypeAnomalousActivity FindingType = "ANOMALOUS_ACTIVITY" FindingTypeAccessViolation FindingType = "ACCESS_VIOLATION" FindingTypeDataExfiltration FindingType = "DATA_EXFILTRATION" FindingTypePrivilegeEscalation FindingType = "PRIVILEGE_ESCALATION" ) // FindingSeverity represents finding severity levels type FindingSeverity string const ( FindingSeverityInfo FindingSeverity = "INFO" FindingSeverityLow FindingSeverity = "LOW" FindingSeverityMedium FindingSeverity = "MEDIUM" FindingSeverityHigh FindingSeverity = "HIGH" FindingSeverityCritical FindingSeverity = "CRITICAL" ) // TimelineEvent represents an event in investigation timeline type TimelineEvent struct { Timestamp time.Time `json:"timestamp"` EventType string `json:"event_type"` Description string `json:"description"` Actor string `json:"actor"` Source string `json:"source"` Metadata map[string]interface{} `json:"metadata"` } // Evidence represents digital evidence type Evidence struct { ID string `json:"id"` Type EvidenceType `json:"type"` Source string `json:"source"` Hash string `json:"hash"` // SHA256 hash for integrity Path string `json:"path"` Size int64 `json:"size"` CollectedAt time.Time `json:"collected_at"` Description string `json:"description"` Metadata map[string]interface{} `json:"metadata"` } // EvidenceType represents types of evidence type EvidenceType string const ( EvidenceTypeLog EvidenceType = "LOG" EvidenceTypeNetwork EvidenceType = "NETWORK" EvidenceTypeFile EvidenceType = "FILE" EvidenceTypeMemory EvidenceType = "MEMORY" EvidenceTypeTransaction EvidenceType = "TRANSACTION" EvidenceTypeArtifact EvidenceType = "ARTIFACT" ) // Recommendation represents security recommendations type Recommendation struct { ID string `json:"id"` Category RecommendationCategory `json:"category"` Priority RecommendationPriority `json:"priority"` Title string `json:"title"` Description string `json:"description"` Actions []string `json:"actions"` Timeline string `json:"timeline"` Resources []string `json:"resources"` Compliance []string `json:"compliance_frameworks"` Metadata map[string]interface{} `json:"metadata"` } // RecommendationCategory represents recommendation categories type RecommendationCategory string const ( CategoryTechnical RecommendationCategory = "TECHNICAL" CategoryProcedural RecommendationCategory = "PROCEDURAL" CategoryTraining RecommendationCategory = "TRAINING" CategoryCompliance RecommendationCategory = "COMPLIANCE" CategoryMonitoring RecommendationCategory = "MONITORING" ) // RecommendationPriority represents recommendation priorities type RecommendationPriority string const ( PriorityLow RecommendationPriority = "LOW" PriorityMedium RecommendationPriority = "MEDIUM" PriorityHigh RecommendationPriority = "HIGH" PriorityCritical RecommendationPriority = "CRITICAL" ) // RiskAssessment represents risk assessment details type RiskAssessment struct { Impact int `json:"impact"` // 1-5 scale Likelihood int `json:"likelihood"` // 1-5 scale RiskScore float64 `json:"risk_score"` // Calculated risk score RiskLevel string `json:"risk_level"` // LOW, MEDIUM, HIGH, CRITICAL CVSS string `json:"cvss_score"` // CVSS score if applicable Exploitable bool `json:"exploitable"` } // RemediationGuidance provides remediation guidance type RemediationGuidance struct { ImmediateActions []string `json:"immediate_actions"` ShortTerm []string `json:"short_term_actions"` LongTerm []string `json:"long_term_actions"` PreventiveMeasures []string `json:"preventive_measures"` MonitoringPoints []string `json:"monitoring_points"` TestingProcedures []string `json:"testing_procedures"` } // AnalysisReport represents a comprehensive analysis report type AnalysisReport struct { ID string `json:"id"` Title string `json:"title"` GeneratedAt time.Time `json:"generated_at"` Period ReportPeriod `json:"period"` Summary *ReportSummary `json:"summary"` SecurityMetrics *SecurityMetricsReport `json:"security_metrics"` ThreatLandscape *ThreatLandscapeReport `json:"threat_landscape"` Investigations []*Investigation `json:"investigations"` Recommendations []*Recommendation `json:"recommendations"` Appendices []*ReportAppendix `json:"appendices"` Metadata map[string]interface{} `json:"metadata"` } // ReportPeriod represents the reporting period type ReportPeriod struct { StartTime time.Time `json:"start_time"` EndTime time.Time `json:"end_time"` Duration string `json:"duration"` } // ReportSummary provides executive summary type ReportSummary struct { TotalEvents int64 `json:"total_events"` SecurityIncidents int `json:"security_incidents"` CriticalFindings int `json:"critical_findings"` HighFindings int `json:"high_findings"` MediumFindings int `json:"medium_findings"` LowFindings int `json:"low_findings"` OverallRiskScore float64 `json:"overall_risk_score"` SecurityPosture string `json:"security_posture"` KeyFindings []string `json:"key_findings"` ExecutiveSummary string `json:"executive_summary"` } // SecurityMetricsReport provides detailed security metrics type SecurityMetricsReport struct { AuthenticationEvents *EventMetrics `json:"authentication_events"` AuthorizationEvents *EventMetrics `json:"authorization_events"` NetworkEvents *EventMetrics `json:"network_events"` TransactionEvents *EventMetrics `json:"transaction_events"` AnomalyEvents *EventMetrics `json:"anomaly_events"` ErrorEvents *EventMetrics `json:"error_events"` } // EventMetrics provides metrics for a specific event type type EventMetrics struct { Total int64 `json:"total"` Successful int64 `json:"successful"` Failed int64 `json:"failed"` Blocked int64 `json:"blocked"` Suspicious int64 `json:"suspicious"` SuccessRate float64 `json:"success_rate"` FailureRate float64 `json:"failure_rate"` TrendAnalysis *TrendAnalysis `json:"trend_analysis"` TopSources map[string]int64 `json:"top_sources"` TimeDistribution map[string]int64 `json:"time_distribution"` } // ThreatLandscapeReport provides threat landscape analysis type ThreatLandscapeReport struct { EmergingThreats []*ThreatIntelligence `json:"emerging_threats"` ActiveCampaigns []*AttackCampaign `json:"active_campaigns"` VulnerabilityTrends []*VulnerabilityTrend `json:"vulnerability_trends"` GeographicAnalysis *GeographicAnalysis `json:"geographic_analysis"` IndustryComparison *IndustryComparison `json:"industry_comparison"` } // ThreatIntelligence represents threat intelligence data type ThreatIntelligence struct { ThreatID string `json:"threat_id"` Name string `json:"name"` Type string `json:"type"` Severity string `json:"severity"` Description string `json:"description"` Indicators []string `json:"indicators"` MitreTactics []string `json:"mitre_tactics"` AffectedSystems []string `json:"affected_systems"` FirstSeen time.Time `json:"first_seen"` LastSeen time.Time `json:"last_seen"` Confidence float64 `json:"confidence"` } // AttackCampaign represents an attack campaign type AttackCampaign struct { CampaignID string `json:"campaign_id"` Name string `json:"name"` Attribution string `json:"attribution"` StartDate time.Time `json:"start_date"` EndDate *time.Time `json:"end_date,omitempty"` Tactics []string `json:"tactics"` Techniques []string `json:"techniques"` Targets []string `json:"targets"` Impact string `json:"impact"` Indicators []string `json:"indicators"` Metadata map[string]interface{} `json:"metadata"` } // VulnerabilityTrend represents vulnerability trend data type VulnerabilityTrend struct { CVE string `json:"cve"` Severity string `json:"severity"` CVSS float64 `json:"cvss_score"` PublishedDate time.Time `json:"published_date"` ExploitExists bool `json:"exploit_exists"` InTheWild bool `json:"in_the_wild"` AffectedAssets int `json:"affected_assets"` PatchAvailable bool `json:"patch_available"` } // GeographicAnalysis provides geographic threat analysis type GeographicAnalysis struct { TopSourceCountries map[string]int64 `json:"top_source_countries"` RegionalTrends map[string]float64 `json:"regional_trends"` HighRiskRegions []string `json:"high_risk_regions"` } // IndustryComparison provides industry comparison data type IndustryComparison struct { IndustryAverage float64 `json:"industry_average"` PeerComparison map[string]float64 `json:"peer_comparison"` BenchmarkMetrics map[string]float64 `json:"benchmark_metrics"` RankingPercentile float64 `json:"ranking_percentile"` } // ReportAppendix represents report appendices type ReportAppendix struct { Title string `json:"title"` Type string `json:"type"` Content string `json:"content"` References []string `json:"references"` Attachments []string `json:"attachments"` Metadata map[string]interface{} `json:"metadata"` } // NewAuditAnalyzer creates a new audit log analyzer func NewAuditAnalyzer(logger *logger.Logger, config *AnalyzerConfig) *AuditAnalyzer { if config == nil { config = &AnalyzerConfig{ AuditLogPaths: []string{"./logs/audit.log"}, OutputDirectory: "./reports", ArchiveDirectory: "./archive", TimeWindow: 24 * time.Hour, SuspiciousThreshold: 0.7, AlertThreshold: 10, MaxLogSize: 100 * 1024 * 1024, // 100MB EnablePatternDetection: true, GenerateReports: true, ReportFormats: []string{"json", "html"}, ReportSchedule: 24 * time.Hour, RetentionPeriod: 30 * 24 * time.Hour, AutoInvestigate: true, InvestigationDepth: 3, } } analyzer := &AuditAnalyzer{ logger: logger, config: config, patterns: make(map[string]*regexp.Regexp), investigations: make([]*Investigation, 0), reports: make([]*AnalysisReport, 0), } // Initialize security patterns analyzer.initializePatterns() return analyzer } // initializePatterns initializes security detection patterns func (aa *AuditAnalyzer) initializePatterns() { // Common security patterns securityPatterns := map[string]string{ "failed_auth": `(?i)(authentication|auth)\s+(failed|failure|denied)`, "privilege_escalation": `(?i)(privilege|sudo|admin|root)\s+(escalat|elevat|gain)`, "suspicious_activity": `(?i)(suspicious|anomal|unusual|irregular)`, "data_exfiltration": `(?i)(exfiltrat|extract|download|export)\s+(data|file|information)`, "brute_force": `(?i)(brute\s*force|password\s+spray|credential\s+stuff)`, "injection_attack": `(?i)(sql\s+injection|xss|script\s+injection|command\s+injection)`, "malware_activity": `(?i)(malware|virus|trojan|backdoor|rootkit)`, "network_anomaly": `(?i)(network\s+anomaly|traffic\s+spike|ddos|dos)`, "access_violation": `(?i)(access\s+denied|unauthorized|forbidden|blocked)`, "key_compromise": `(?i)(key\s+compromise|credential\s+leak|private\s+key)`, } for name, pattern := range securityPatterns { if compiled, err := regexp.Compile(pattern); err == nil { aa.patterns[name] = compiled } else { aa.logger.Warn(fmt.Sprintf("Failed to compile pattern %s: %v", name, err)) } } // Add custom patterns from config for i, pattern := range aa.config.CustomPatterns { name := fmt.Sprintf("custom_%d", i) if compiled, err := regexp.Compile(pattern); err == nil { aa.patterns[name] = compiled } else { aa.logger.Warn(fmt.Sprintf("Failed to compile custom pattern %s: %v", pattern, err)) } } } // AnalyzeLogs performs comprehensive analysis of audit logs func (aa *AuditAnalyzer) AnalyzeLogs() (*AnalysisReport, error) { aa.logger.Info("Starting comprehensive audit log analysis") report := &AnalysisReport{ ID: fmt.Sprintf("report_%d", time.Now().Unix()), Title: "Security Audit Log Analysis Report", GeneratedAt: time.Now(), Period: ReportPeriod{ StartTime: time.Now().Add(-aa.config.TimeWindow), EndTime: time.Now(), Duration: aa.config.TimeWindow.String(), }, Metadata: make(map[string]interface{}), } // Process each log file var allEvents []*LogEvent for _, logPath := range aa.config.AuditLogPaths { events, err := aa.processLogFile(logPath) if err != nil { aa.logger.Warn(fmt.Sprintf("Failed to process log file %s: %v", logPath, err)) continue } allEvents = append(allEvents, events...) } aa.logger.Info(fmt.Sprintf("Processed %d log events", len(allEvents))) // Perform analysis report.Summary = aa.generateSummary(allEvents) report.SecurityMetrics = aa.generateSecurityMetrics(allEvents) report.ThreatLandscape = aa.generateThreatLandscape(allEvents) // Auto-investigate if enabled if aa.config.AutoInvestigate { investigations := aa.autoInvestigate(allEvents) report.Investigations = investigations aa.investigations = append(aa.investigations, investigations...) } // Generate recommendations report.Recommendations = aa.generateRecommendations(allEvents, report.Summary) // Store report aa.reports = append(aa.reports, report) // Generate report files if aa.config.GenerateReports { err := aa.generateReportFiles(report) if err != nil { aa.logger.Warn(fmt.Sprintf("Failed to generate report files: %v", err)) } } aa.logger.Info("Completed audit log analysis") return report, nil } // LogEvent represents a parsed log event type LogEvent struct { Timestamp time.Time `json:"timestamp"` Level string `json:"level"` Source string `json:"source"` Message string `json:"message"` EventType string `json:"event_type"` Actor string `json:"actor"` Action string `json:"action"` Resource string `json:"resource"` Result string `json:"result"` IPAddress string `json:"ip_address"` UserAgent string `json:"user_agent"` Metadata map[string]interface{} `json:"metadata"` Severity int `json:"severity"` // 1-5 Suspicious bool `json:"suspicious"` PatternHits []string `json:"pattern_hits"` } // processLogFile processes a single log file func (aa *AuditAnalyzer) processLogFile(logPath string) ([]*LogEvent, error) { file, err := os.Open(logPath) if err != nil { return nil, fmt.Errorf("failed to open log file: %w", err) } defer file.Close() // Check file size stat, err := file.Stat() if err != nil { return nil, fmt.Errorf("failed to stat log file: %w", err) } if stat.Size() > aa.config.MaxLogSize { aa.logger.Warn(fmt.Sprintf("Log file %s exceeds max size, processing last %d bytes", logPath, aa.config.MaxLogSize)) // Seek to last MaxLogSize bytes _, err = file.Seek(-aa.config.MaxLogSize, io.SeekEnd) if err != nil { return nil, fmt.Errorf("failed to seek in log file: %w", err) } } var events []*LogEvent scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() if event := aa.parseLogLine(line); event != nil { // Apply time window filter if event.Timestamp.After(time.Now().Add(-aa.config.TimeWindow)) { events = append(events, event) } } } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("error scanning log file: %w", err) } return events, nil } // parseLogLine parses a single log line into a LogEvent func (aa *AuditAnalyzer) parseLogLine(line string) *LogEvent { if strings.TrimSpace(line) == "" { return nil } // Try to parse as JSON first var jsonEvent map[string]interface{} if err := json.Unmarshal([]byte(line), &jsonEvent); err == nil { return aa.parseJSONEvent(jsonEvent) } // Parse as structured text return aa.parseTextEvent(line) } // parseJSONEvent parses a JSON log event func (aa *AuditAnalyzer) parseJSONEvent(data map[string]interface{}) *LogEvent { event := &LogEvent{ Metadata: make(map[string]interface{}), PatternHits: make([]string, 0), } // Extract standard fields if ts, ok := data["timestamp"].(string); ok { if parsed, err := time.Parse(time.RFC3339, ts); err == nil { event.Timestamp = parsed } } if event.Timestamp.IsZero() { event.Timestamp = time.Now() } event.Level = aa.getStringField(data, "level", "info") event.Source = aa.getStringField(data, "source", "unknown") event.Message = aa.getStringField(data, "message", "") event.EventType = aa.getStringField(data, "event_type", "general") event.Actor = aa.getStringField(data, "actor", "") event.Action = aa.getStringField(data, "action", "") event.Resource = aa.getStringField(data, "resource", "") event.Result = aa.getStringField(data, "result", "") event.IPAddress = aa.getStringField(data, "ip_address", "") event.UserAgent = aa.getStringField(data, "user_agent", "") // Copy all metadata for k, v := range data { event.Metadata[k] = v } // Analyze patterns and determine suspiciousness aa.analyzeEventPatterns(event) return event } // parseTextEvent parses a text log event func (aa *AuditAnalyzer) parseTextEvent(line string) *LogEvent { event := &LogEvent{ Timestamp: time.Now(), Level: "info", Source: "text_log", Message: line, EventType: "general", Metadata: make(map[string]interface{}), PatternHits: make([]string, 0), } // Try to extract timestamp from common formats timePatterns := []string{ `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}`, // ISO format `\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}`, // Standard format `\w{3} \d{2} \d{2}:\d{2}:\d{2}`, // Syslog format } for _, pattern := range timePatterns { if re := regexp.MustCompile(pattern); re != nil { if match := re.FindString(line); match != "" { if parsed, err := time.Parse("2006-01-02T15:04:05", match); err == nil { event.Timestamp = parsed break } else if parsed, err := time.Parse("2006-01-02 15:04:05", match); err == nil { event.Timestamp = parsed break } else if parsed, err := time.Parse("Jan 02 15:04:05", match); err == nil { // Add current year for syslog format now := time.Now() event.Timestamp = time.Date(now.Year(), parsed.Month(), parsed.Day(), parsed.Hour(), parsed.Minute(), parsed.Second(), 0, time.Local) break } } } } // Extract log level levelPattern := regexp.MustCompile(`(?i)\[(DEBUG|INFO|WARN|ERROR|FATAL)\]`) if match := levelPattern.FindStringSubmatch(line); len(match) > 1 { event.Level = strings.ToLower(match[1]) } // Extract IP addresses ipPattern := regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`) if match := ipPattern.FindString(line); match != "" { event.IPAddress = match } // Analyze patterns and determine suspiciousness aa.analyzeEventPatterns(event) return event } // analyzeEventPatterns analyzes event against security patterns func (aa *AuditAnalyzer) analyzeEventPatterns(event *LogEvent) { suspiciousScore := 0.0 content := strings.ToLower(event.Message + " " + event.Action + " " + event.Result) for patternName, pattern := range aa.patterns { if pattern.MatchString(content) { event.PatternHits = append(event.PatternHits, patternName) suspiciousScore += aa.getPatternWeight(patternName) } } // Calculate severity based on pattern hits and content event.Severity = aa.calculateSeverity(event.Level, event.PatternHits) event.Suspicious = suspiciousScore >= aa.config.SuspiciousThreshold } // getPatternWeight returns the weight of a security pattern func (aa *AuditAnalyzer) getPatternWeight(patternName string) float64 { weights := map[string]float64{ "failed_auth": 0.3, "privilege_escalation": 0.8, "suspicious_activity": 0.6, "data_exfiltration": 0.9, "brute_force": 0.7, "injection_attack": 0.8, "malware_activity": 0.9, "network_anomaly": 0.5, "access_violation": 0.4, "key_compromise": 1.0, } if weight, exists := weights[patternName]; exists { return weight } return 0.5 // Default weight for custom patterns } // calculateSeverity calculates event severity func (aa *AuditAnalyzer) calculateSeverity(level string, patternHits []string) int { baseSeverity := map[string]int{ "debug": 1, "info": 2, "warn": 3, "error": 4, "fatal": 5, } severity := baseSeverity[level] if severity == 0 { severity = 2 } // Increase severity based on pattern hits for _, pattern := range patternHits { switch pattern { case "key_compromise", "data_exfiltration", "malware_activity": severity = 5 case "privilege_escalation", "injection_attack", "brute_force": if severity < 4 { severity = 4 } case "suspicious_activity", "network_anomaly": if severity < 3 { severity = 3 } } } return severity } // getStringField safely extracts string field from map func (aa *AuditAnalyzer) getStringField(data map[string]interface{}, key, defaultValue string) string { if value, ok := data[key].(string); ok { return value } return defaultValue } // generateSummary generates report summary func (aa *AuditAnalyzer) generateSummary(events []*LogEvent) *ReportSummary { summary := &ReportSummary{ TotalEvents: int64(len(events)), KeyFindings: make([]string, 0), } // Count findings by severity for _, event := range events { switch event.Severity { case 5: summary.CriticalFindings++ case 4: summary.HighFindings++ case 3: summary.MediumFindings++ case 1, 2: summary.LowFindings++ } if event.Suspicious { summary.SecurityIncidents++ } } // Calculate overall risk score totalFindings := summary.CriticalFindings + summary.HighFindings + summary.MediumFindings + summary.LowFindings if totalFindings > 0 { summary.OverallRiskScore = float64(summary.CriticalFindings*5+summary.HighFindings*4+summary.MediumFindings*3+summary.LowFindings*1) / float64(totalFindings*5) * 100 } // Determine security posture if summary.OverallRiskScore >= 80 { summary.SecurityPosture = "CRITICAL" } else if summary.OverallRiskScore >= 60 { summary.SecurityPosture = "HIGH_RISK" } else if summary.OverallRiskScore >= 40 { summary.SecurityPosture = "MEDIUM_RISK" } else if summary.OverallRiskScore >= 20 { summary.SecurityPosture = "LOW_RISK" } else { summary.SecurityPosture = "GOOD" } // Generate key findings if summary.CriticalFindings > 0 { summary.KeyFindings = append(summary.KeyFindings, fmt.Sprintf("%d critical security findings require immediate attention", summary.CriticalFindings)) } if summary.SecurityIncidents > 0 { summary.KeyFindings = append(summary.KeyFindings, fmt.Sprintf("%d suspicious security incidents detected", summary.SecurityIncidents)) } // Generate executive summary summary.ExecutiveSummary = aa.generateExecutiveSummary(summary) return summary } // generateExecutiveSummary generates executive summary text func (aa *AuditAnalyzer) generateExecutiveSummary(summary *ReportSummary) string { return fmt.Sprintf( "During the analysis period, %d log events were processed. %d security incidents were identified with %d critical and %d high severity findings. "+ "The overall security posture is assessed as %s with a risk score of %.1f/100. "+ "Immediate attention is required for critical findings, and enhanced monitoring is recommended for detected anomalies.", summary.TotalEvents, summary.SecurityIncidents, summary.CriticalFindings, summary.HighFindings, summary.SecurityPosture, summary.OverallRiskScore, ) } // generateSecurityMetrics generates detailed security metrics func (aa *AuditAnalyzer) generateSecurityMetrics(events []*LogEvent) *SecurityMetricsReport { metrics := &SecurityMetricsReport{ AuthenticationEvents: aa.calculateEventMetrics(events, "authentication"), AuthorizationEvents: aa.calculateEventMetrics(events, "authorization"), NetworkEvents: aa.calculateEventMetrics(events, "network"), TransactionEvents: aa.calculateEventMetrics(events, "transaction"), AnomalyEvents: aa.calculateEventMetrics(events, "anomaly"), ErrorEvents: aa.calculateEventMetrics(events, "error"), } return metrics } // calculateEventMetrics calculates metrics for specific event type func (aa *AuditAnalyzer) calculateEventMetrics(events []*LogEvent, eventType string) *EventMetrics { metrics := &EventMetrics{ TopSources: make(map[string]int64), TimeDistribution: make(map[string]int64), } var relevantEvents []*LogEvent for _, event := range events { if strings.Contains(strings.ToLower(event.EventType), eventType) || strings.Contains(strings.ToLower(event.Message), eventType) { relevantEvents = append(relevantEvents, event) } } metrics.Total = int64(len(relevantEvents)) for _, event := range relevantEvents { // Count by result result := strings.ToLower(event.Result) switch { case strings.Contains(result, "success") || strings.Contains(result, "ok"): metrics.Successful++ case strings.Contains(result, "fail") || strings.Contains(result, "error"): metrics.Failed++ case strings.Contains(result, "block") || strings.Contains(result, "deny"): metrics.Blocked++ } if event.Suspicious { metrics.Suspicious++ } // Track top sources source := event.IPAddress if source == "" { source = event.Source } metrics.TopSources[source]++ // Track time distribution (hourly) hour := event.Timestamp.Format("15") metrics.TimeDistribution[hour]++ } // Calculate rates if metrics.Total > 0 { metrics.SuccessRate = float64(metrics.Successful) / float64(metrics.Total) * 100 metrics.FailureRate = float64(metrics.Failed) / float64(metrics.Total) * 100 } return metrics } // generateThreatLandscape generates threat landscape analysis func (aa *AuditAnalyzer) generateThreatLandscape(events []*LogEvent) *ThreatLandscapeReport { report := &ThreatLandscapeReport{ EmergingThreats: aa.identifyEmergingThreats(events), ActiveCampaigns: aa.identifyActiveCampaigns(events), VulnerabilityTrends: aa.analyzeVulnerabilityTrends(events), GeographicAnalysis: aa.analyzeGeography(events), IndustryComparison: aa.generateIndustryComparison(events), } return report } // identifyEmergingThreats identifies emerging threats from log data func (aa *AuditAnalyzer) identifyEmergingThreats(events []*LogEvent) []*ThreatIntelligence { var threats []*ThreatIntelligence // Example threat identification logic patternCounts := make(map[string]int) for _, event := range events { for _, pattern := range event.PatternHits { patternCounts[pattern]++ } } for pattern, count := range patternCounts { if count >= aa.config.AlertThreshold { threat := &ThreatIntelligence{ ThreatID: fmt.Sprintf("threat_%s_%d", pattern, time.Now().Unix()), Name: strings.Title(strings.ReplaceAll(pattern, "_", " ")), Type: "BEHAVIORAL", Severity: aa.getSeverityFromPattern(pattern), Description: fmt.Sprintf("Elevated activity detected for %s pattern", pattern), FirstSeen: time.Now().Add(-aa.config.TimeWindow), LastSeen: time.Now(), Confidence: aa.calculateConfidenceScore(count), } threats = append(threats, threat) } } return threats } // identifyActiveCampaigns identifies active attack campaigns func (aa *AuditAnalyzer) identifyActiveCampaigns(events []*LogEvent) []*AttackCampaign { var campaigns []*AttackCampaign // Group events by IP and analyze patterns ipEvents := make(map[string][]*LogEvent) for _, event := range events { if event.IPAddress != "" { ipEvents[event.IPAddress] = append(ipEvents[event.IPAddress], event) } } for ip, eventsFromIP := range ipEvents { if len(eventsFromIP) >= aa.config.AlertThreshold { // Analyze if this represents a campaign suspiciousCount := 0 for _, event := range eventsFromIP { if event.Suspicious { suspiciousCount++ } } if float64(suspiciousCount)/float64(len(eventsFromIP)) >= aa.config.SuspiciousThreshold { campaign := &AttackCampaign{ CampaignID: fmt.Sprintf("campaign_%s_%d", ip, time.Now().Unix()), Name: fmt.Sprintf("Suspicious Activity from %s", ip), Attribution: "Unknown", StartDate: eventsFromIP[0].Timestamp, Tactics: aa.extractTactics(eventsFromIP), Impact: aa.assessImpact(eventsFromIP), Indicators: []string{ip}, } campaigns = append(campaigns, campaign) } } } return campaigns } // Helper methods for threat analysis func (aa *AuditAnalyzer) getSeverityFromPattern(pattern string) string { highSeverityPatterns := []string{"key_compromise", "data_exfiltration", "malware_activity"} for _, highPattern := range highSeverityPatterns { if pattern == highPattern { return "HIGH" } } return "MEDIUM" } func (aa *AuditAnalyzer) calculateConfidenceScore(count int) float64 { // Simple confidence calculation based on event count confidence := float64(count) / float64(aa.config.AlertThreshold*5) if confidence > 1.0 { confidence = 1.0 } return confidence } func (aa *AuditAnalyzer) extractTactics(events []*LogEvent) []string { tacticsSet := make(map[string]bool) for _, event := range events { for _, pattern := range event.PatternHits { switch pattern { case "failed_auth", "brute_force": tacticsSet["Credential Access"] = true case "privilege_escalation": tacticsSet["Privilege Escalation"] = true case "data_exfiltration": tacticsSet["Exfiltration"] = true case "injection_attack": tacticsSet["Execution"] = true } } } var tactics []string for tactic := range tacticsSet { tactics = append(tactics, tactic) } return tactics } func (aa *AuditAnalyzer) assessImpact(events []*LogEvent) string { criticalCount := 0 for _, event := range events { if event.Severity >= 4 { criticalCount++ } } if criticalCount >= len(events)/2 { return "HIGH" } else if criticalCount > 0 { return "MEDIUM" } return "LOW" } func (aa *AuditAnalyzer) analyzeVulnerabilityTrends(events []*LogEvent) []*VulnerabilityTrend { // Placeholder for vulnerability trend analysis return []*VulnerabilityTrend{} } func (aa *AuditAnalyzer) analyzeGeography(events []*LogEvent) *GeographicAnalysis { countryCounts := make(map[string]int64) for _, event := range events { if event.IPAddress != "" { // In a real implementation, you would use a GeoIP service // For now, just group by IP ranges parts := strings.Split(event.IPAddress, ".") if len(parts) >= 2 { region := fmt.Sprintf("%s.%s.x.x", parts[0], parts[1]) countryCounts[region]++ } } } return &GeographicAnalysis{ TopSourceCountries: countryCounts, RegionalTrends: make(map[string]float64), HighRiskRegions: []string{}, } } func (aa *AuditAnalyzer) generateIndustryComparison(events []*LogEvent) *IndustryComparison { // Placeholder for industry comparison return &IndustryComparison{ IndustryAverage: 75.0, PeerComparison: make(map[string]float64), BenchmarkMetrics: make(map[string]float64), RankingPercentile: 80.0, } } // autoInvestigate automatically creates investigations for suspicious events func (aa *AuditAnalyzer) autoInvestigate(events []*LogEvent) []*Investigation { var investigations []*Investigation // Group suspicious events by type and source suspiciousGroups := aa.groupSuspiciousEvents(events) for groupKey, groupEvents := range suspiciousGroups { if len(groupEvents) >= aa.config.AlertThreshold { investigation := aa.createInvestigation(groupKey, groupEvents) investigations = append(investigations, investigation) } } return investigations } // groupSuspiciousEvents groups suspicious events for investigation func (aa *AuditAnalyzer) groupSuspiciousEvents(events []*LogEvent) map[string][]*LogEvent { groups := make(map[string][]*LogEvent) for _, event := range events { if event.Suspicious { // Group by primary pattern hit and source var groupKey string if len(event.PatternHits) > 0 { groupKey = fmt.Sprintf("%s_%s", event.PatternHits[0], event.IPAddress) } else { groupKey = fmt.Sprintf("suspicious_%s", event.IPAddress) } groups[groupKey] = append(groups[groupKey], event) } } return groups } // createInvestigation creates an investigation from grouped events func (aa *AuditAnalyzer) createInvestigation(groupKey string, events []*LogEvent) *Investigation { investigation := &Investigation{ ID: fmt.Sprintf("inv_%s_%d", groupKey, time.Now().Unix()), Title: fmt.Sprintf("Suspicious Activity Investigation: %s", groupKey), Description: fmt.Sprintf("Automated investigation created for suspicious activity pattern: %s", groupKey), Severity: aa.calculateInvestigationSeverity(events), Status: StatusOpen, CreatedAt: time.Now(), UpdatedAt: time.Now(), AssignedTo: "security_team", RelatedEvents: make([]string, 0), Findings: aa.generateFindings(events), Timeline: aa.generateTimeline(events), Evidence: aa.collectEvidence(events), Recommendations: aa.generateInvestigationRecommendations(events), Metadata: make(map[string]interface{}), } // Add event IDs to related events for i := range events { investigation.RelatedEvents = append(investigation.RelatedEvents, fmt.Sprintf("event_%d", i)) } return investigation } // calculateInvestigationSeverity calculates investigation severity func (aa *AuditAnalyzer) calculateInvestigationSeverity(events []*LogEvent) InvestigationSeverity { maxSeverity := 0 for _, event := range events { if event.Severity > maxSeverity { maxSeverity = event.Severity } } switch maxSeverity { case 5: return SeverityCritical case 4: return SeverityHigh case 3: return SeverityMedium default: return SeverityLow } } // generateFindings generates security findings from events func (aa *AuditAnalyzer) generateFindings(events []*LogEvent) []*Finding { var findings []*Finding // Group events by pattern patternGroups := make(map[string][]*LogEvent) for _, event := range events { for _, pattern := range event.PatternHits { patternGroups[pattern] = append(patternGroups[pattern], event) } } for pattern, patternEvents := range patternGroups { finding := &Finding{ ID: fmt.Sprintf("finding_%s_%d", pattern, time.Now().Unix()), Type: aa.getFindingType(pattern), Severity: aa.getFindingSeverity(pattern), Title: strings.Title(strings.ReplaceAll(pattern, "_", " ")) + " Detected", Description: fmt.Sprintf("Multiple instances of %s pattern detected", pattern), Evidence: make([]string, 0), MITRE: aa.getMITRETactics(pattern), Risk: aa.calculateRiskAssessment(patternEvents), Remediation: aa.getRemediationGuidance(pattern), CreatedAt: time.Now(), Metadata: map[string]interface{}{"pattern": pattern, "event_count": len(patternEvents)}, } findings = append(findings, finding) } return findings } // getFindingType maps pattern to finding type func (aa *AuditAnalyzer) getFindingType(pattern string) FindingType { typeMap := map[string]FindingType{ "failed_auth": FindingTypeAccessViolation, "privilege_escalation": FindingTypePrivilegeEscalation, "data_exfiltration": FindingTypeDataExfiltration, "injection_attack": FindingTypeVulnerability, "suspicious_activity": FindingTypeAnomalousActivity, "access_violation": FindingTypeAccessViolation, } if findingType, exists := typeMap[pattern]; exists { return findingType } return FindingTypeAnomalousActivity } // getFindingSeverity maps pattern to finding severity func (aa *AuditAnalyzer) getFindingSeverity(pattern string) FindingSeverity { severityMap := map[string]FindingSeverity{ "key_compromise": FindingSeverityCritical, "data_exfiltration": FindingSeverityCritical, "privilege_escalation": FindingSeverityHigh, "injection_attack": FindingSeverityHigh, "brute_force": FindingSeverityMedium, "suspicious_activity": FindingSeverityMedium, "access_violation": FindingSeverityLow, } if severity, exists := severityMap[pattern]; exists { return severity } return FindingSeverityMedium } // getMITRETactics maps pattern to MITRE ATT&CK tactics func (aa *AuditAnalyzer) getMITRETactics(pattern string) []string { tacticsMap := map[string][]string{ "failed_auth": {"TA0006"}, // Credential Access "privilege_escalation": {"TA0004"}, // Privilege Escalation "data_exfiltration": {"TA0010"}, // Exfiltration "injection_attack": {"TA0002"}, // Execution "brute_force": {"TA0006"}, // Credential Access } if tactics, exists := tacticsMap[pattern]; exists { return tactics } return []string{} } // calculateRiskAssessment calculates risk assessment for events func (aa *AuditAnalyzer) calculateRiskAssessment(events []*LogEvent) RiskAssessment { // Calculate impact and likelihood based on events impact := 3 // Default medium impact likelihood := 3 // Default medium likelihood // Adjust based on event severity and frequency maxSeverity := 0 for _, event := range events { if event.Severity > maxSeverity { maxSeverity = event.Severity } } impact = maxSeverity if len(events) > 10 { likelihood = 5 } else if len(events) > 5 { likelihood = 4 } riskScore := float64(impact*likelihood) / 25.0 * 100 // Scale to 0-100 var riskLevel string switch { case riskScore >= 80: riskLevel = "CRITICAL" case riskScore >= 60: riskLevel = "HIGH" case riskScore >= 40: riskLevel = "MEDIUM" default: riskLevel = "LOW" } return RiskAssessment{ Impact: impact, Likelihood: likelihood, RiskScore: riskScore, RiskLevel: riskLevel, Exploitable: impact >= 4 && likelihood >= 3, } } // getRemediationGuidance provides remediation guidance for patterns func (aa *AuditAnalyzer) getRemediationGuidance(pattern string) RemediationGuidance { guidanceMap := map[string]RemediationGuidance{ "failed_auth": { ImmediateActions: []string{"Review failed authentication attempts", "Check for account lockouts"}, ShortTerm: []string{"Implement account lockout policies", "Enable MFA"}, LongTerm: []string{"Deploy advanced authentication monitoring"}, PreventiveMeasures: []string{"Regular password policy reviews", "User awareness training"}, MonitoringPoints: []string{"Authentication logs", "Account lockout events"}, }, "privilege_escalation": { ImmediateActions: []string{"Review privilege escalation attempts", "Check system integrity"}, ShortTerm: []string{"Implement privilege monitoring", "Review admin access"}, LongTerm: []string{"Deploy privilege access management"}, PreventiveMeasures: []string{"Least privilege principle", "Regular access reviews"}, MonitoringPoints: []string{"Privilege changes", "Admin command execution"}, }, } if guidance, exists := guidanceMap[pattern]; exists { return guidance } return RemediationGuidance{ ImmediateActions: []string{"Investigate the security event"}, ShortTerm: []string{"Implement monitoring for this pattern"}, LongTerm: []string{"Review security policies"}, } } // generateTimeline generates investigation timeline func (aa *AuditAnalyzer) generateTimeline(events []*LogEvent) []*TimelineEvent { var timeline []*TimelineEvent // Sort events by timestamp sort.Slice(events, func(i, j int) bool { return events[i].Timestamp.Before(events[j].Timestamp) }) for _, event := range events { timelineEvent := &TimelineEvent{ Timestamp: event.Timestamp, EventType: event.EventType, Description: event.Message, Actor: event.Actor, Source: event.Source, Metadata: event.Metadata, } timeline = append(timeline, timelineEvent) } return timeline } // collectEvidence collects evidence from events func (aa *AuditAnalyzer) collectEvidence(events []*LogEvent) []*Evidence { var evidence []*Evidence // Create evidence entries for each unique source sources := make(map[string]bool) for _, event := range events { if event.Source != "" && !sources[event.Source] { sources[event.Source] = true ev := &Evidence{ ID: fmt.Sprintf("evidence_%s_%d", event.Source, time.Now().Unix()), Type: EvidenceTypeLog, Source: event.Source, CollectedAt: time.Now(), Description: fmt.Sprintf("Log evidence from %s", event.Source), Metadata: map[string]interface{}{ "event_count": len(events), "time_range": fmt.Sprintf("%v to %v", events[0].Timestamp, events[len(events)-1].Timestamp), }, } evidence = append(evidence, ev) } } return evidence } // generateInvestigationRecommendations generates recommendations for investigation func (aa *AuditAnalyzer) generateInvestigationRecommendations(events []*LogEvent) []*Recommendation { var recommendations []*Recommendation // Analyze patterns and generate specific recommendations patternCounts := make(map[string]int) for _, event := range events { for _, pattern := range event.PatternHits { patternCounts[pattern]++ } } for pattern, count := range patternCounts { if count >= 5 { // Only recommend for frequent patterns rec := &Recommendation{ ID: fmt.Sprintf("rec_%s_%d", pattern, time.Now().Unix()), Category: CategoryTechnical, Priority: aa.getRecommendationPriority(pattern), Title: fmt.Sprintf("Address %s Pattern", strings.Title(strings.ReplaceAll(pattern, "_", " "))), Description: fmt.Sprintf("Multiple instances of %s detected (%d events). Immediate action required.", pattern, count), Actions: aa.getRecommendationActions(pattern), Timeline: "Immediate", Metadata: map[string]interface{}{"pattern": pattern, "count": count}, } recommendations = append(recommendations, rec) } } return recommendations } // getRecommendationPriority maps pattern to recommendation priority func (aa *AuditAnalyzer) getRecommendationPriority(pattern string) RecommendationPriority { priorityMap := map[string]RecommendationPriority{ "key_compromise": PriorityCritical, "data_exfiltration": PriorityCritical, "privilege_escalation": PriorityHigh, "injection_attack": PriorityHigh, "brute_force": PriorityMedium, "suspicious_activity": PriorityMedium, } if priority, exists := priorityMap[pattern]; exists { return priority } return PriorityMedium } // getRecommendationActions provides specific actions for patterns func (aa *AuditAnalyzer) getRecommendationActions(pattern string) []string { actionsMap := map[string][]string{ "failed_auth": { "Review authentication logs", "Implement account lockout policies", "Enable multi-factor authentication", "Monitor for continued failed attempts", }, "privilege_escalation": { "Investigate privilege escalation attempts", "Review user privileges and access rights", "Implement privilege access management", "Monitor administrative activities", }, "data_exfiltration": { "Immediately investigate data access patterns", "Review data loss prevention policies", "Monitor network traffic for anomalies", "Implement data classification and protection", }, } if actions, exists := actionsMap[pattern]; exists { return actions } return []string{ "Investigate the security pattern", "Implement monitoring for this activity", "Review relevant security policies", } } // generateRecommendations generates general recommendations from analysis func (aa *AuditAnalyzer) generateRecommendations(events []*LogEvent, summary *ReportSummary) []*Recommendation { var recommendations []*Recommendation // Generate recommendations based on overall findings if summary.CriticalFindings > 0 { rec := &Recommendation{ ID: fmt.Sprintf("rec_critical_%d", time.Now().Unix()), Category: CategoryTechnical, Priority: PriorityCritical, Title: "Address Critical Security Findings", Description: fmt.Sprintf("%d critical security findings require immediate attention", summary.CriticalFindings), Actions: []string{ "Immediately investigate all critical findings", "Implement emergency response procedures", "Escalate to security leadership", "Document incident response actions", }, Timeline: "Immediate (0-4 hours)", } recommendations = append(recommendations, rec) } if summary.SecurityIncidents > 10 { rec := &Recommendation{ ID: fmt.Sprintf("rec_incidents_%d", time.Now().Unix()), Category: CategoryMonitoring, Priority: PriorityHigh, Title: "Enhanced Security Monitoring", Description: fmt.Sprintf("%d security incidents detected - enhanced monitoring recommended", summary.SecurityIncidents), Actions: []string{ "Deploy additional security monitoring tools", "Increase log collection and analysis", "Implement real-time alerting", "Review detection rules and thresholds", }, Timeline: "Short-term (1-2 weeks)", } recommendations = append(recommendations, rec) } if summary.OverallRiskScore > 70 { rec := &Recommendation{ ID: fmt.Sprintf("rec_risk_%d", time.Now().Unix()), Category: CategoryProcedural, Priority: PriorityHigh, Title: "Risk Management Review", Description: fmt.Sprintf("Overall risk score of %.1f requires comprehensive risk review", summary.OverallRiskScore), Actions: []string{ "Conduct comprehensive risk assessment", "Review and update security policies", "Implement additional security controls", "Schedule regular security reviews", }, Timeline: "Medium-term (2-4 weeks)", } recommendations = append(recommendations, rec) } return recommendations } // generateReportFiles generates report files in configured formats func (aa *AuditAnalyzer) generateReportFiles(report *AnalysisReport) error { // Ensure output directory exists if err := os.MkdirAll(aa.config.OutputDirectory, 0755); err != nil { return fmt.Errorf("failed to create output directory: %w", err) } for _, format := range aa.config.ReportFormats { filename := fmt.Sprintf("%s/%s.%s", aa.config.OutputDirectory, report.ID, format) switch format { case "json": err := aa.generateJSONReport(report, filename) if err != nil { aa.logger.Warn(fmt.Sprintf("Failed to generate JSON report: %v", err)) } case "html": err := aa.generateHTMLReport(report, filename) if err != nil { aa.logger.Warn(fmt.Sprintf("Failed to generate HTML report: %v", err)) } case "csv": err := aa.generateCSVReport(report, filename) if err != nil { aa.logger.Warn(fmt.Sprintf("Failed to generate CSV report: %v", err)) } } } return nil } // generateJSONReport generates JSON format report func (aa *AuditAnalyzer) generateJSONReport(report *AnalysisReport, filename string) error { data, err := json.MarshalIndent(report, "", " ") if err != nil { return fmt.Errorf("failed to marshal report: %w", err) } return os.WriteFile(filename, data, 0644) } // generateHTMLReport generates HTML format report func (aa *AuditAnalyzer) generateHTMLReport(report *AnalysisReport, filename string) error { // Basic HTML template - in production, use a proper template engine html := fmt.Sprintf(`
Generated: %s
Period: %s to %s
%s
| Metric | Value |
|---|---|
| Total Events | %d |
| Security Incidents | %d |
| Critical Findings | %d |
| High Findings | %d |
| Medium Findings | %d |
| Low Findings | %d |
| Overall Risk Score | %.1f/100 |
| Security Posture | %s |
%d active investigations
%d recommendations generated