Files
Redflag/docs/4_LOG/November_2025/analysis/analysis.md

18 KiB

RedFlag Agent Command Handling System - Architecture Analysis

Executive Summary

The agent implements a modular but primarily monolithic scanning architecture. While scanner implementations are isolated into separate files, the orchestration of scanning (the handleScanUpdates function) is a large, tightly-coupled function that combines all subsystems in a single control flow. Storage and system info gathering are separate, but not formally separated as distinct subsystems that can be independently managed.


1. Command Processing Pipeline

Entry Point: Main Agent Loop

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go Lines: 410-549 (main check-in loop)

The agent continuously loops, checking in with the server and processing commands:

for {
    // Lines 412-414: Add jitter
    jitter := time.Duration(rand.Intn(30)) * time.Second
    time.Sleep(jitter)
    
    // Lines 417-425: System info update every hour
    if time.Since(lastSystemInfoUpdate) >= systemInfoUpdateInterval {
        // Call reportSystemInfo()
    }
    
    // Lines 465-490: GetCommands from server with optional metrics
    commands, err := apiClient.GetCommands(cfg.AgentID, metrics)
    
    // Lines 499-544: Switch on command type
    for _, cmd := range commands {
        switch cmd.Type {
        case "scan_updates":
            handleScanUpdates(...)
        case "collect_specs":
        case "dry_run_update":
        case "install_updates":
        case "confirm_dependencies":
        case "enable_heartbeat":
        case "disable_heartbeat":
        case "reboot":
        }
    }
    
    // Line 547: Wait for next check-in
    time.Sleep(...)
}

Command Types Supported

  1. scan_updates - Main focus (lines 503-506)
  2. collect_specs (not implemented)
  3. dry_run_update (lines 511-514)
  4. install_updates (lines 516-519)
  5. confirm_dependencies (lines 521-524)
  6. enable_heartbeat (lines 526-529)
  7. disable_heartbeat (lines 531-534)
  8. reboot (lines 537-540)

2. MONOLITHIC scan_updates Implementation

Location and Size

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go Function: handleScanUpdates() Lines: 551-709 (159 lines)

The Monolith Problem

The function is a single, large, sequential orchestrator that tightly couples all scanning subsystems:

handleScanUpdates()
├─ APT Scanner (lines 559-574)
│  ├─ IsAvailable() check
│  ├─ Scan()
│  └─ Error handling + accumulation
│
├─ DNF Scanner (lines 576-592)
│  ├─ IsAvailable() check
│  ├─ Scan()
│  └─ Error handling + accumulation
│
├─ Docker Scanner (lines 594-610)
│  ├─ IsAvailable() check
│  ├─ Scan()
│  └─ Error handling + accumulation
│
├─ Windows Update Scanner (lines 612-628)
│  ├─ IsAvailable() check
│  ├─ Scan()
│  └─ Error handling + accumulation
│
├─ Winget Scanner (lines 630-646)
│  ├─ IsAvailable() check
│  ├─ Scan()
│  └─ Error handling + accumulation
│
├─ Report Building (lines 648-677)
│  ├─ Combine all errors
│  ├─ Build scan log report
│  └─ Report to server
│
└─ Update Reporting (lines 686-708)
   ├─ Report updates if found
   └─ Return errors

Key Issues with Current Architecture

  1. No Abstraction Layer: Each scanner is called directly with repeated if available -> scan -> handle error blocks
  2. Sequential Execution: All scanners run one-by-one (lines 559-646) - no parallelization
  3. Tight Coupling: Error handling logic is mixed with business logic
  4. No Subsystem State Management: Cannot track individual subsystem health or readiness
  5. Repeated Code: Same pattern repeated 5 times for different scanners

Code Pattern Repetition (Example - APT):

// Lines 559-574: APT pattern
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")
}

This exact pattern repeats for DNF, Docker, Windows, and Winget scanners.


3. Scanner Implementations (Modular)

3.1 APT Scanner

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/apt.go Lines: 1-91

Interface Implementation:

  • IsAvailable() - Checks if apt command exists (line 23-26)
  • Scan() - Returns []client.UpdateReportItem (lines 29-42)
  • parseAPTOutput() - Helper function (lines 44-90)

Key Behavior:

  • Runs apt-get update (optional, line 31)
  • Runs apt list --upgradable (line 35)
  • Parses output with regex (line 50)
  • Determines severity based on repository name (lines 69-71)

Severity Logic:

severity := "moderate"
if strings.Contains(repository, "security") {
    severity = "important"
}

3.2 DNF Scanner

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/dnf.go Lines: 1-157

Interface Implementation:

  • IsAvailable() - Checks if dnf command exists (lines 23-26)
  • Scan() - Returns []client.UpdateReportItem (lines 29-43)
  • parseDNFOutput() - Parses output (lines 45-108)
  • getInstalledVersion() - Queries RPM (lines 111-118)
  • determineSeverity() - Complex logic (lines 121-157)

Severity Determination (lines 121-157):

  • Security keywords: critical
  • Kernel updates: important
  • Core system packages (glibc, systemd, bash): important
  • Development tools: moderate
  • Default: low

3.3 Docker Scanner

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/docker.go Lines: 1-163

Interface Implementation:

  • IsAvailable() - Checks docker command + daemon ping (lines 34-47)
  • Scan() - Returns []client.UpdateReportItem (lines 50-123)
  • checkForUpdate() - Compare local vs remote digests (lines 137-154)
  • Close() - Close Docker client (lines 157-162)

Key Behavior:

  • Lists all containers (line 54)
  • Gets image inspect details (line 72)
  • Calls registry client for remote digest (line 86)
  • Compares digest hashes to detect updates (line 151)

RegistryClient Subsystem (registry.go, lines 1-260):

  • Handles Docker Registry HTTP API v2
  • Caches manifest responses (5 min TTL)
  • Parses image names into registry/repository
  • Gets authentication tokens for Docker Hub
  • Supports manifest digest extraction

3.4 Windows Update Scanner (WUA API)

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/windows_wua.go Lines: 1-553

Interface Implementation:

  • IsAvailable() - Returns true only on Windows (lines 27-30)
  • Scan() - Returns []client.UpdateReportItem (lines 33-67)
  • Windows-specific COM integration (lines 38-43)
  • Conversion methods (lines 70-211)

Key Behavior:

  • Initializes COM for Windows Update Agent API (lines 38-43)
  • Creates update session and searcher (lines 46-55)
  • Searches with criteria: "IsInstalled=0 AND IsHidden=0" (line 58)
  • Converts WUA results with rich metadata (lines 90-211)

Metadata Extraction (lines 112-186):

  • KB articles
  • Update identity
  • Security bulletins (includes CVEs)
  • MSRC severity
  • Download size
  • Deployment dates
  • More info URLs
  • Release notes
  • Categories

Severity Mapping (lines 463-479):

  • MSRC critical/important → critical
  • MSRC moderate → moderate
  • MSRC low → low

3.5 Winget Scanner

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/winget.go Lines: 1-662

Interface Implementation:

  • IsAvailable() - Windows-only, checks winget command (lines 34-43)
  • Scan() - Multi-method with fallbacks (lines 46-84)
  • Multiple scan methods for resilience (lines 87-178)
  • Package parsing (lines 279-508)

Key Behavior - Multiple Scan Methods:

  1. Method 1: scanWithJSON() - Primary, JSON output (lines 87-122)
  2. Method 2: scanWithBasicOutput() - Fallback, text parsing (lines 125-134)
  3. Method 3: attemptWingetRecovery() - Recovery procedures (lines 533-576)

Recovery Procedures (lines 533-576):

  • Reset winget sources
  • Update winget itself
  • Repair Windows App Installer
  • Scan with admin privileges

Severity Determination (lines 324-371):

  • Security tools: critical
  • Browsers/communication: high
  • Development tools: moderate
  • Microsoft Store apps: low
  • Default: moderate

Package Categorization (lines 374-484):

  • Development, Security, Browser, Communication, Media, Productivity, Utility, Gaming, Application

4. System Info and Storage Integration

4.1 System Info Collection

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/system/info.go Lines: 1-100+ (first 100 shown)

SystemInfo Structure (lines 13-28):

type SystemInfo struct {
    Hostname       string
    OSType         string
    OSVersion      string
    OSArchitecture string
    AgentVersion   string
    IPAddress      string
    CPUInfo        CPUInfo
    MemoryInfo     MemoryInfo
    DiskInfo       []DiskInfo          // MODULAR: Multiple disks!
    RunningProcesses int
    Uptime         string
    RebootRequired bool
    RebootReason   string
    Metadata       map[string]string
}

DiskInfo Structure (lines 45-57):

type DiskInfo struct {
    Mountpoint    string
    Total         uint64
    Available     uint64
    Used          uint64
    UsedPercent   float64
    Filesystem    string
    IsRoot        bool        // Primary system disk
    IsLargest     bool        // Largest storage disk
    DiskType      string      // SSD, HDD, NVMe, etc.
    Device        string      // Block device name
}

4.2 System Info Reporting

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go Function: reportSystemInfo() Lines: 1357-1407

Reporting Frequency:

  • Lines 407-408: const systemInfoUpdateInterval = 1 * time.Hour
  • Lines 417-425: Updates hourly during main loop

What Gets Reported:

  • CPU model, cores, threads
  • Memory total/used/percent
  • Disk total/used/percent (primary disk)
  • IP address
  • Process count
  • Uptime
  • OS type/version/architecture
  • All metadata from SystemInfo

4.3 Local Cache Subsystem

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/cache/local.go

Key Functions:

  • Load() - Load cache from disk
  • UpdateScanResults() - Store latest scan results
  • SetAgentInfo() - Store agent metadata
  • SetAgentStatus() - Update status
  • Save() - Persist cache to disk

5. Lightweight Metrics vs Full System Info

5.1 Lightweight Metrics (Every Check-in)

File: /home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go Lines: 429-444

What Gets Collected Every Check-in:

sysMetrics, err := system.GetLightweightMetrics()
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,
    }
}

5.2 Full System Info (Hourly)

Lines: 417-425 + reportSystemInfo function

Difference: Full info includes CPU model, detailed disk info, process count, IP address, and more detailed metadata


6. Current Modularity Assessment

Modular (Good):

  1. Scanner Implementations: Each scanner is a separate file with its own logic
  2. Registry Client: Docker registry communication is separated
  3. System Info: Platform-specific implementations split (windows.go, windows_stub.go, windows_wua.go, etc.)
  4. Installers: Separate installer implementations per package type
  5. Local Cache: Separate subsystem for caching

Monolithic (Bad):

  1. handleScanUpdates(): Tight coupling of all scanners in one function
  2. Command Processing: All command types in a single switch statement
  3. Error Aggregation: No formal error handling subsystem; just accumulates strings
  4. No Subsystem Health Tracking: Can't individually monitor scanner status
  5. No Parallelization: Scanners run sequentially, wasting time
  6. Logging Mixed with Logic: Log statements interleaved with business logic

7. Key Data Flow Paths

Path 1: scan_updates Command

GetCommands()
    ↓
switch cmd.Type == "scan_updates"
    ↓
handleScanUpdates()
    ├─ aptScanner.Scan() → UpdateReportItem[]
    ├─ dnfScanner.Scan() → UpdateReportItem[]
    ├─ dockerScanner.Scan() → UpdateReportItem[] (includes registryClient)
    ├─ windowsUpdateScanner.Scan() → UpdateReportItem[]
    ├─ wingetScanner.Scan() → UpdateReportItem[] (with recovery procedures)
    ├─ Combine all updates
    ├─ ReportLog() [scan summary]
    └─ ReportUpdates() [actual updates]

Path 2: Local Scan via CLI

Lines: 712-805, handleScanCommand()

  • Same scanner initialization and execution
  • Save results to cache
  • Display via display.PrintScanResults()

Path 3: System Metrics Reporting

Main Loop (every check-in)
    ├─ GetLightweightMetrics() [every 5-300 sec]
    └─ Every hour:
        ├─ GetSystemInfo() [detailed]
        ├─ ReportSystemInfo() [to server]

8. File Structure Summary

Core Agent

aggregator-agent/
├── cmd/agent/
│   └── main.go                           [ENTRY POINT - 1510 lines]
│       ├─ registerAgent() [266-348]
│       ├─ runAgent() [387-549]            [MAIN LOOP]
│       ├─ handleScanUpdates() [551-709]   [MONOLITHIC]
│       ├─ handleScanCommand() [712-805]
│       ├─ handleStatusCommand() [808-846]
│       ├─ handleListUpdatesCommand() [849-871]
│       ├─ handleInstallUpdates() [873-989]
│       ├─ handleDryRunUpdate() [992-1105]
│       ├─ handleConfirmDependencies() [1108-1216]
│       ├─ handleEnableHeartbeat() [1219-1291]
│       ├─ handleDisableHeartbeat() [1294-1355]
│       ├─ reportSystemInfo() [1357-1407]
│       └─ handleReboot() [1410-1495]

Scanners

internal/scanner/
├── apt.go              [91 lines]  - APT package manager
├── dnf.go              [157 lines] - DNF/RPM package manager
├── docker.go           [163 lines] - Docker image scanning
├── registry.go         [260 lines] - Docker Registry API client
├── windows.go          [Stub for non-Windows]
├── windows_wua.go      [553 lines] - Windows Update Agent API
├── winget.go           [662 lines] - Windows package manager
└── windows_override.go [Overrides for Windows builds]

System & Supporting

internal/
├── system/
│   ├── info.go         [100+ lines] - System information gathering
│   └── windows.go      [Windows-specific system info]
├── cache/
│   └── local.go        [Local caching of scan results]
├── client/
│   └── client.go       [API communication]
├── config/
│   └── config.go       [Configuration management]
├── installer/
│   ├── installer.go    [Factory pattern]
│   ├── apt.go
│   ├── dnf.go
│   ├── docker.go
│   ├── windows.go
│   └── winget.go
├── service/
│   ├── service_stub.go
│   └── windows.go      [Windows service management]
└── display/
    └── terminal.go     [Terminal display utilities]

9. Summary of Architecture Findings

Subsystems Included in scan_updates

  1. APT Scanner - Linux Debian/Ubuntu package updates
  2. DNF Scanner - Linux Fedora/RHEL package updates
  3. Docker Scanner - Container image updates (with Registry subsystem)
  4. Windows Update Scanner - Windows OS updates (WUA API)
  5. Winget Scanner - Windows application updates

Integration Model

Not a subsystem architecture, but rather:

  • Sequential execution of isolated scanner modules
  • Error accumulation without formal subsystem health tracking
  • Sequential reporting - all errors reported together at end
  • No dependency management between subsystems
  • No resource pooling (each scanner creates its own connections)

Monolithic Aspects

The handleScanUpdates() function exhibits monolithic characteristics:

  • Single responsibility is violated (orchestrates 5+ distinct scanning systems)
  • Tight coupling between orchestrator and scanners
  • Repeated code patterns suggest missing abstraction
  • No separation of concerns between:
    • Scanner availability checking
    • Actual scanning
    • Error handling
    • Result aggregation
    • Reporting

Modular Aspects

The individual scanner implementations ARE modular:

  • Each scanner has own file
  • Each implements common interface (IsAvailable, Scan)
  • Each scanner logic is isolated
  • Registry client is separated from Docker scanner
  • Platform-specific code is separated (windows_wua.go vs windows.go stub)

Recommendations for Refactoring

If modularity/subsystem architecture is desired:

  1. Create ScannerRegistry/Factory - Manage scanner lifecycle
  2. Extract orchestration logic - Create ScanOrchestrator interface
  3. Implement health tracking - Track subsystem readiness
  4. Enable parallelization - Run scanners concurrently
  5. Formal error handling - Per-subsystem error types
  6. Dependency injection - Inject scanners into handlers
  7. Configuration per subsystem - Enable/disable individual scanners
  8. Metrics/observability - Track scan duration, success rate per subsystem