Files
Redflag/docs/3_BACKLOG/P1-001_Agent-Install-ID-Parsing-Issue.md

6.7 KiB

P1-001: Agent Install ID Parsing Issue

Priority: P1 (Major) Source Reference: From needsfixingbeforepush.md line 3 Date Identified: 2025-11-12

Problem Description

The generateInstallScript function in downloads.go is not properly extracting the agent_id query parameter, causing the install script to always generate new agent IDs instead of using existing registered agent IDs for upgrades.

Current Behavior

Install script downloads always generate new UUIDs instead of preserving existing agent IDs:

# BEFORE (broken)
curl -sfL "http://localhost:8080/api/v1/install/linux?agent_id=6fdba4c92c4d4d33a4010e98db0df72d8bbe3d62c6b7e0a33cef3325e29bdd6d"
# Result: AGENT_ID="cf865204-125a-491d-976f-5829b6c081e6" (NEW UUID generated)

Expected Behavior

For upgrade scenarios, the install script should preserve the existing agent ID passed via query parameter:

# AFTER (fixed)
curl -sfL "http://localhost:8080/api/v1/install/linux?agent_id=6fdba4c92c4d4d33a4010e98db0df72d8bbe3d62c6b7e0a33cef3325e29bdd6d"
# Result: AGENT_ID="6fdba4c92c4d4d33a4010e98db0df72d8bbe3d62c6b7e0a33cef3325e29bdd6d" (PASSED UUID)

Root Cause Analysis

The generateInstallScript function only looks at query parameters but doesn't properly validate/extract the UUID format from the agent_id parameter. The function likely ignores or fails to parse the existing agent ID, falling back to generating a new UUID each time.

Proposed Solution

Implement proper agent ID parsing with security validation following this priority order:

  1. Header: X-Agent-ID (most secure, not exposed in URLs/logs)
  2. Path: /api/v1/install/:platform/:agent_id (legacy support)
  3. Query: ?agent_id=uuid (fallback for current usage)

All paths must:

  • Validate UUID format before using
  • Enforce rate limiting on agent ID reuse
  • Apply signature validation for security

Implementation Details

// Example fix in downloads.go
func generateInstallScript(c *gin.Context) (string, error) {
    var agentID string

    // Priority 1: Check header (most secure)
    if agentID = c.GetHeader("X-Agent-ID"); agentID != "" {
        if isValidUUID(agentID) {
            // Use header agent ID
        }
    }

    // Priority 2: Check path parameter
    if agentID == "" {
        if agentID = c.Param("agent_id"); agentID != "" {
            if isValidUUID(agentID) {
                // Use path agent ID
            }
        }
    }

    // Priority 3: Check query parameter (current broken behavior)
    if agentID == "" {
        if agentID = c.Query("agent_id"); agentID != "" {
            if isValidUUID(agentID) {
                // Use query agent ID
            }
        }
    }

    // Fallback: Generate new UUID if no valid agent ID provided
    if agentID == "" {
        agentID = generateNewUUID()
    }

    // Generate install script with the determined agent ID
    return generateScriptTemplate(agentID), nil
}

Definition of Done

  • Install script preserves existing agent ID when provided via query parameter
  • Agent ID format validation (UUID v4) prevents malformed IDs
  • New UUID generated only when no valid agent ID is provided
  • Security validation prevents agent ID spoofing
  • Rate limiting prevents abuse of agent ID reuse
  • Backward compatibility maintained for existing install methods

Test Plan

  1. Query Parameter Test:

    # Test with valid UUID in query parameter
    TEST_UUID="6fdba4c92c4d4d33a4010e98db0df72d8bbe3d62c6b7e0a33cef3325e29bdd6d"
    
    curl -sfL "http://localhost:8080/api/v1/install/linux?agent_id=$TEST_UUID" | grep "AGENT_ID="
    
    # Expected: AGENT_ID="$TEST_UUID" (same UUID)
    # Not: AGENT_ID="<new-generated-uuid>"
    
  2. Invalid UUID Test:

    # Test with malformed UUID
    curl -sfL "http://localhost:8080/api/v1/install/linux?agent_id=invalid-uuid" | grep "AGENT_ID="
    
    # Expected: AGENT_ID="<new-generated-uuid>" (rejects invalid, generates new)
    
  3. Empty Parameter Test:

    # Test with empty agent_id parameter
    curl -sfL "http://localhost:8080/api/v1/install/linux?agent_id=" | grep "AGENT_ID="
    
    # Expected: AGENT_ID="<new-generated-uuid>" (empty treated as not provided)
    
  4. No Parameter Test:

    # Test without agent_id parameter (current behavior)
    curl -sfL "http://localhost:8080/api/v1/install/linux" | grep "AGENT_ID="
    
    # Expected: AGENT_ID="<new-generated-uuid>" (maintain backward compatibility)
    
  5. Security Validation Test:

    # Test with UUID validation edge cases
    curl -sfL "http://localhost:8080/api/v1/install/linux?agent_id=00000000-0000-0000-0000-000000000000" | grep "AGENT_ID="
    
    # Should handle edge cases appropriately
    

Files to Modify

  • aggregator-server/internal/api/handlers/downloads.go (main fix location)
  • Add UUID validation utility functions
  • Potentially update rate limiting logic for agent ID reuse
  • Add tests for install script generation

Impact

  • Agent Upgrades: Prevents agent identity loss during upgrades/reinstallation
  • Agent Management: Maintains consistent agent identity across system lifecycle
  • Audit Trail: Preserves agent history and command continuity
  • User Experience: Allows seamless agent reinstallation without re-registration

Security Considerations

  • Agent ID Spoofing: Must validate that agent ID belongs to legitimate agent
  • Rate Limiting: Prevent abuse of agent ID reuse for malicious purposes
  • Signature Validation: Ensure agent ID requests are authenticated
  • Audit Logging: Log agent ID reuse attempts for security monitoring

Upgrade Scenario Use Case

# Agent needs upgrade/reinstallation on same machine
# Admin provides existing agent ID to preserve history
EXISTING_AGENT_ID="6fdba4c92c4d4d33a4010e98db0df72d8bbe3d62c6b7e0a33cef3325e29bdd6d"

# Install script preserves agent identity
curl -sfL "http://redflag-server:8080/api/v1/install/linux?agent_id=$EXISTING_AGENT_ID" | sudo bash

# Result: Agent reinstalls with same ID, preserving:
# - Command history
# - Configuration settings
# - Agent registration record
# - Audit trail continuity

Verification Commands

After fix implementation:

# Verify query parameter preservation
UUID="6fdba4c92c4d4d33a4010e98db0df72d8bbe3d62c6b7e0a33cef3325e29bdd6d"
SCRIPT=$(curl -sfL "http://localhost:8080/api/v1/install/linux?agent_id=$UUID")
echo "$SCRIPT" | grep "AGENT_ID="

# Should output: AGENT_ID="6fdba4c92c4d4d33a4010e98db0df72d8bbe3d62c6b7e0a33cef3325e29bdd6d"

# Test invalid UUID rejection
INVALID_SCRIPT=$(curl -sfL "http://localhost:8080/api/v1/install/linux?agent_id=invalid")
echo "$INVALID_SCRIPT" | grep "AGENT_ID="

# Should output different UUID (generated new)