Files

562 lines
18 KiB
Markdown

# 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:
```go
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):
```go
// 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**:
```go
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):
```go
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):
```go
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**:
```go
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