# RedFlag Agent - Code Implementation Examples ## 1. Main Loop (Entry Point) **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go` **Lines**: 410-549 The agent's main loop runs continuously, checking in with the server at regular intervals: ```go // Lines 410-549: Main check-in loop for { // Add jitter to prevent thundering herd jitter := time.Duration(rand.Intn(30)) * time.Second time.Sleep(jitter) // Check if we need to send detailed system info update (hourly) if time.Since(lastSystemInfoUpdate) >= systemInfoUpdateInterval { log.Printf("Updating detailed system information...") if err := reportSystemInfo(apiClient, cfg); err != nil { log.Printf("Failed to report system info: %v\n", err) } else { lastSystemInfoUpdate = time.Now() log.Printf("✓ System information updated\n") } } log.Printf("Checking in with server... (Agent v%s)", AgentVersion) // Collect lightweight system metrics (every check-in) sysMetrics, err := system.GetLightweightMetrics() var metrics *client.SystemMetrics if err == nil { metrics = &client.SystemMetrics{ CPUPercent: sysMetrics.CPUPercent, MemoryPercent: sysMetrics.MemoryPercent, MemoryUsedGB: sysMetrics.MemoryUsedGB, MemoryTotalGB: sysMetrics.MemoryTotalGB, DiskUsedGB: sysMetrics.DiskUsedGB, DiskTotalGB: sysMetrics.DiskTotalGB, DiskPercent: sysMetrics.DiskPercent, Uptime: sysMetrics.Uptime, Version: AgentVersion, } } // Get commands from server commands, err := apiClient.GetCommands(cfg.AgentID, metrics) if err != nil { // Handle token renewal if needed // ... error handling code ... } // Process each command for _, cmd := range commands { log.Printf("Processing command: %s (%s)\n", cmd.Type, cmd.ID) switch cmd.Type { case "scan_updates": if err := handleScanUpdates(...); err != nil { log.Printf("Error scanning updates: %v\n", err) } case "install_updates": if err := handleInstallUpdates(...); err != nil { log.Printf("Error installing updates: %v\n", err) } // ... other command types ... } } // Wait for next check-in time.Sleep(time.Duration(getCurrentPollingInterval(cfg)) * time.Second) } ``` --- ## 2. The Monolithic handleScanUpdates Function **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go` **Lines**: 551-709 This function orchestrates all scanner subsystems in a monolithic manner: ```go func handleScanUpdates( apiClient *client.Client, cfg *config.Config, aptScanner *scanner.APTScanner, dnfScanner *scanner.DNFScanner, dockerScanner *scanner.DockerScanner, windowsUpdateScanner *scanner.WindowsUpdateScanner, wingetScanner *scanner.WingetScanner, commandID string) error { log.Println("Scanning for updates...") var allUpdates []client.UpdateReportItem var scanErrors []string var scanResults []string // MONOLITHIC PATTERN 1: APT Scanner (lines 559-574) if aptScanner.IsAvailable() { log.Println(" - Scanning APT packages...") updates, err := aptScanner.Scan() if err != nil { errorMsg := fmt.Sprintf("APT scan failed: %v", err) log.Printf(" %s\n", errorMsg) scanErrors = append(scanErrors, errorMsg) } else { resultMsg := fmt.Sprintf("Found %d APT updates", len(updates)) log.Printf(" %s\n", resultMsg) scanResults = append(scanResults, resultMsg) allUpdates = append(allUpdates, updates...) } } else { scanResults = append(scanResults, "APT scanner not available") } // MONOLITHIC PATTERN 2: DNF Scanner (lines 576-592) // [SAME PATTERN REPEATS - lines 576-592] if dnfScanner.IsAvailable() { log.Println(" - Scanning DNF packages...") updates, err := dnfScanner.Scan() if err != nil { errorMsg := fmt.Sprintf("DNF scan failed: %v", err) log.Printf(" %s\n", errorMsg) scanErrors = append(scanErrors, errorMsg) } else { resultMsg := fmt.Sprintf("Found %d DNF updates", len(updates)) log.Printf(" %s\n", resultMsg) scanResults = append(scanResults, resultMsg) allUpdates = append(allUpdates, updates...) } } else { scanResults = append(scanResults, "DNF scanner not available") } // MONOLITHIC PATTERN 3: Docker Scanner (lines 594-610) // [SAME PATTERN REPEATS - lines 594-610] if dockerScanner != nil && dockerScanner.IsAvailable() { log.Println(" - Scanning Docker images...") updates, err := dockerScanner.Scan() if err != nil { errorMsg := fmt.Sprintf("Docker scan failed: %v", err) log.Printf(" %s\n", errorMsg) scanErrors = append(scanErrors, errorMsg) } else { resultMsg := fmt.Sprintf("Found %d Docker image updates", len(updates)) log.Printf(" %s\n", resultMsg) scanResults = append(scanResults, resultMsg) allUpdates = append(allUpdates, updates...) } } else { scanResults = append(scanResults, "Docker scanner not available") } // MONOLITHIC PATTERN 4: Windows Update Scanner (lines 612-628) // [SAME PATTERN REPEATS - lines 612-628] if windowsUpdateScanner.IsAvailable() { log.Println(" - Scanning Windows updates...") updates, err := windowsUpdateScanner.Scan() if err != nil { errorMsg := fmt.Sprintf("Windows Update scan failed: %v", err) log.Printf(" %s\n", errorMsg) scanErrors = append(scanErrors, errorMsg) } else { resultMsg := fmt.Sprintf("Found %d Windows updates", len(updates)) log.Printf(" %s\n", resultMsg) scanResults = append(scanResults, resultMsg) allUpdates = append(allUpdates, updates...) } } else { scanResults = append(scanResults, "Windows Update scanner not available") } // MONOLITHIC PATTERN 5: Winget Scanner (lines 630-646) // [SAME PATTERN REPEATS - lines 630-646] if wingetScanner.IsAvailable() { log.Println(" - Scanning Winget packages...") updates, err := wingetScanner.Scan() if err != nil { errorMsg := fmt.Sprintf("Winget scan failed: %v", err) log.Printf(" %s\n", errorMsg) scanErrors = append(scanErrors, errorMsg) } else { resultMsg := fmt.Sprintf("Found %d Winget package updates", len(updates)) log.Printf(" %s\n", resultMsg) scanResults = append(scanResults, resultMsg) allUpdates = append(allUpdates, updates...) } } else { scanResults = append(scanResults, "Winget scanner not available") } // Report scan results (lines 648-677) success := len(allUpdates) > 0 || len(scanErrors) == 0 var combinedOutput string if len(scanResults) > 0 { combinedOutput += "Scan Results:\n" + strings.Join(scanResults, "\n") } if len(scanErrors) > 0 { if combinedOutput != "" { combinedOutput += "\n" } combinedOutput += "Scan Errors:\n" + strings.Join(scanErrors, "\n") } if len(allUpdates) > 0 { if combinedOutput != "" { combinedOutput += "\n" } combinedOutput += fmt.Sprintf("Total Updates Found: %d", len(allUpdates)) } // Create scan log entry logReport := client.LogReport{ CommandID: commandID, Action: "scan_updates", Result: map[bool]string{true: "success", false: "failure"}[success], Stdout: combinedOutput, Stderr: strings.Join(scanErrors, "\n"), ExitCode: map[bool]int{true: 0, false: 1}[success], DurationSeconds: 0, } // Report the scan log if err := apiClient.ReportLog(cfg.AgentID, logReport); err != nil { log.Printf("Failed to report scan log: %v\n", err) } // Report updates (lines 686-708) if len(allUpdates) > 0 { report := client.UpdateReport{ CommandID: commandID, Timestamp: time.Now(), Updates: allUpdates, } if err := apiClient.ReportUpdates(cfg.AgentID, report); err != nil { return fmt.Errorf("failed to report updates: %w", err) } log.Printf("✓ Reported %d updates to server\n", len(allUpdates)) } else { log.Println("✓ No updates found") } return nil } ``` **Key Issues**: 1. Pattern repeats 5 times verbatim (lines 559-646) 2. No abstraction for common scanner pattern 3. Sequential execution (each scanner waits for previous) 4. Tight coupling between orchestrator and individual scanners 5. All error handling mixed with business logic --- ## 3. Modular Scanner - APT Implementation **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/apt.go` **Lines**: 1-91 Individual scanners ARE modular: ```go package scanner // APTScanner scans for APT package updates type APTScanner struct{} // NewAPTScanner creates a new APT scanner func NewAPTScanner() *APTScanner { return &APTScanner{} } // IsAvailable checks if APT is available on this system func (s *APTScanner) IsAvailable() bool { _, err := exec.LookPath("apt") return err == nil } // Scan scans for available APT updates func (s *APTScanner) Scan() ([]client.UpdateReportItem, error) { // Update package cache (sudo may be required, but try anyway) updateCmd := exec.Command("apt-get", "update") updateCmd.Run() // Ignore errors // Get upgradable packages cmd := exec.Command("apt", "list", "--upgradable") output, err := cmd.Output() if err != nil { return nil, fmt.Errorf("failed to run apt list: %w", err) } return parseAPTOutput(output) } func parseAPTOutput(output []byte) ([]client.UpdateReportItem, error) { var updates []client.UpdateReportItem scanner := bufio.NewScanner(bytes.NewReader(output)) // Regex to parse apt output re := regexp.MustCompile(`^([^\s/]+)/([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+\[upgradable from:\s+([^\]]+)\]`) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "Listing...") { continue } matches := re.FindStringSubmatch(line) if len(matches) < 6 { continue } packageName := matches[1] repository := matches[2] newVersion := matches[3] oldVersion := matches[5] // Determine severity (simplified) severity := "moderate" if strings.Contains(repository, "security") { severity = "important" } update := client.UpdateReportItem{ PackageType: "apt", PackageName: packageName, CurrentVersion: oldVersion, AvailableVersion: newVersion, Severity: severity, RepositorySource: repository, Metadata: map[string]interface{}{ "architecture": matches[4], }, } updates = append(updates, update) } return updates, nil } ``` **Good Aspects**: - Self-contained in single file - Clear interface (IsAvailable, Scan) - No dependencies on other scanners - Error handling encapsulated - Could be swapped out easily --- ## 4. Complex Scanner - Windows Update (WUA API) **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/windows_wua.go` **Lines**: 33-67, 70-211 ```go // Scan scans for available Windows updates using WUA API func (s *WindowsUpdateScannerWUA) Scan() ([]client.UpdateReportItem, error) { if !s.IsAvailable() { return nil, fmt.Errorf("WUA scanner is only available on Windows") } // Initialize COM comshim.Add(1) defer comshim.Done() ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_SPEED_OVER_MEMORY) defer ole.CoUninitialize() // Create update session session, err := windowsupdate.NewUpdateSession() if err != nil { return nil, fmt.Errorf("failed to create Windows Update session: %w", err) } // Create update searcher searcher, err := session.CreateUpdateSearcher() if err != nil { return nil, fmt.Errorf("failed to create update searcher: %w", err) } // Search for available updates (IsInstalled=0 means not installed) searchCriteria := "IsInstalled=0 AND IsHidden=0" result, err := searcher.Search(searchCriteria) if err != nil { return nil, fmt.Errorf("failed to search for updates: %w", err) } // Convert results to our format updates := s.convertWUAResult(result) return updates, nil } // Convert results - rich metadata extraction (lines 70-211) func (s *WindowsUpdateScannerWUA) convertWUAUpdate(update *windowsupdate.IUpdate) *client.UpdateReportItem { // Get update information title := update.Title description := update.Description kbArticles := s.getKBArticles(update) updateIdentity := update.Identity // Use MSRC severity if available severity := s.mapMsrcSeverity(update.MsrcSeverity) if severity == "" { severity = s.determineSeverityFromCategories(update) } // Create metadata with WUA-specific information metadata := map[string]interface{}{ "package_manager": "windows_update", "detected_via": "wua_api", "kb_articles": kbArticles, "update_identity": updateIdentity.UpdateID, "revision_number": updateIdentity.RevisionNumber, "download_size": update.MaxDownloadSize, "api_source": "windows_update_agent", "scan_timestamp": time.Now().Format(time.RFC3339), } // Add MSRC severity if available if update.MsrcSeverity != "" { metadata["msrc_severity"] = update.MsrcSeverity } // Add security bulletin IDs (includes CVEs) if len(update.SecurityBulletinIDs) > 0 { metadata["security_bulletins"] = update.SecurityBulletinIDs cveList := make([]string, 0) for _, bulletin := range update.SecurityBulletinIDs { if strings.HasPrefix(bulletin, "CVE-") { cveList = append(cveList, bulletin) } } if len(cveList) > 0 { metadata["cve_list"] = cveList } } // ... more metadata extraction ... updateItem := &client.UpdateReportItem{ PackageType: "windows_update", PackageName: title, PackageDescription: description, CurrentVersion: currentVersion, AvailableVersion: availableVersion, Severity: severity, RepositorySource: "Microsoft Update", Metadata: metadata, } return updateItem } ``` **Key Characteristics**: - Complex internal logic but clean external interface - Rich metadata extraction (KB articles, CVEs, MSRC severity) - Windows-specific (COM interop) - Still follows IsAvailable/Scan pattern - Encapsulates complexity --- ## 5. System Info Reporting (Hourly) **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go` **Lines**: 1357-1407 ```go // reportSystemInfo collects and reports detailed system information func reportSystemInfo(apiClient *client.Client, cfg *config.Config) error { // Collect detailed system information sysInfo, err := system.GetSystemInfo(AgentVersion) if err != nil { return fmt.Errorf("failed to get system info: %w", err) } // Create system info report report := client.SystemInfoReport{ Timestamp: time.Now(), CPUModel: sysInfo.CPUInfo.ModelName, CPUCores: sysInfo.CPUInfo.Cores, CPUThreads: sysInfo.CPUInfo.Threads, MemoryTotal: sysInfo.MemoryInfo.Total, DiskTotal: uint64(0), DiskUsed: uint64(0), IPAddress: sysInfo.IPAddress, Processes: sysInfo.RunningProcesses, Uptime: sysInfo.Uptime, Metadata: make(map[string]interface{}), } // Add primary disk info if len(sysInfo.DiskInfo) > 0 { primaryDisk := sysInfo.DiskInfo[0] report.DiskTotal = primaryDisk.Total report.DiskUsed = primaryDisk.Used report.Metadata["disk_mount"] = primaryDisk.Mountpoint report.Metadata["disk_filesystem"] = primaryDisk.Filesystem } // Add metadata report.Metadata["collected_at"] = time.Now().Format(time.RFC3339) report.Metadata["hostname"] = sysInfo.Hostname report.Metadata["os_type"] = sysInfo.OSType report.Metadata["os_version"] = sysInfo.OSVersion report.Metadata["os_architecture"] = sysInfo.OSArchitecture // Report to server if err := apiClient.ReportSystemInfo(cfg.AgentID, report); err != nil { return fmt.Errorf("failed to report system info: %w", err) } return nil } ``` **Timing**: - Runs hourly (line 407-408: `const systemInfoUpdateInterval = 1 * time.Hour`) - Triggered in main loop (lines 417-425) - Separate from scan operations --- ## 6. System Info Data Structures **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/system/info.go` **Lines**: 13-57 ```go // SystemInfo contains detailed system information type SystemInfo struct { Hostname string `json:"hostname"` OSType string `json:"os_type"` OSVersion string `json:"os_version"` OSArchitecture string `json:"os_architecture"` AgentVersion string `json:"agent_version"` IPAddress string `json:"ip_address"` CPUInfo CPUInfo `json:"cpu_info"` MemoryInfo MemoryInfo `json:"memory_info"` DiskInfo []DiskInfo `json:"disk_info"` // MULTIPLE DISKS! RunningProcesses int `json:"running_processes"` Uptime string `json:"uptime"` RebootRequired bool `json:"reboot_required"` RebootReason string `json:"reboot_reason"` Metadata map[string]string `json:"metadata"` } // CPUInfo contains CPU information type CPUInfo struct { ModelName string `json:"model_name"` Cores int `json:"cores"` Threads int `json:"threads"` } // MemoryInfo contains memory information type MemoryInfo struct { Total uint64 `json:"total"` Available uint64 `json:"available"` Used uint64 `json:"used"` UsedPercent float64 `json:"used_percent"` } // DiskInfo contains disk information for modular storage management type DiskInfo struct { Mountpoint string `json:"mountpoint"` Total uint64 `json:"total"` Available uint64 `json:"available"` Used uint64 `json:"used"` UsedPercent float64 `json:"used_percent"` Filesystem string `json:"filesystem"` IsRoot bool `json:"is_root"` // Primary system disk IsLargest bool `json:"is_largest"` // Largest storage disk DiskType string `json:"disk_type"` // SSD, HDD, NVMe, etc. Device string `json:"device"` // Block device name } ``` **Important Notes**: - Supports multiple disks (DiskInfo is a slice) - Each disk tracked separately (mount point, filesystem type, device) - Reports primary (IsRoot) and largest (IsLargest) disk separately - Well-structured for expansion --- ## Summary **Monolithic**: The orchestration function (handleScanUpdates) that combines all scanners **Modular**: Individual scanner implementations and system info collection **Missing**: Formal subsystem abstraction layer and lifecycle management