Add docs and project files - force for Culurien

This commit is contained in:
Fimeg
2026-03-28 20:46:24 -04:00
parent dc61797423
commit 484a7f77ce
343 changed files with 119530 additions and 0 deletions

View File

@@ -0,0 +1,599 @@
# 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