Files
GopherGate/ACTION_PLAN.md
T

5.5 KiB

GopherGate Remediation Action Plan

Based on the findings from the Code Review Report, this document outlines a phased execution plan to fix all security, concurrency, reliability, and code quality issues in GopherGate.


Phase 1: Critical Security & Crash Fixes (Immediate)

Task 1.1: Redact API Keys in Gemini Provider Logs

  • Target File: internal/providers/gemini.go
  • Problem: fmt.Printf("[Gemini] POST %s\n", url) logs the raw request URL containing ?key=AIzaSy..., leaking API secrets to logs.
  • Action Items:
    1. Create a helper function sanitizeURL(rawURL string) string in internal/providers/helpers.go to strip or replace sensitive query parameters (key=...).
    2. Replace all instances of raw URL printing in gemini.go (L378, L636) with log.Printf("[Gemini] POST %s", sanitizeURL(url)).

Task 1.2: Add Thread Safety to Server Providers Map

  • Target File: internal/server/server.go
  • Problem: s.providers map is mutated during RefreshProviders() without mutex locking while HTTP handlers read from it concurrently (selectProvider()).
  • Action Items:
    1. Add providersMu sync.RWMutex field to Server struct in server.go.
    2. Acquire s.providersMu.Lock() before modifying s.providers in RefreshProviders().
    3. Acquire s.providersMu.RLock() in selectProvider() and deferred s.providersMu.RUnlock().

Phase 2: Thread-Safety & Race Condition Hardening

Task 2.1: Synchronize ModelRegistry Lookups and Reloads

  • Target Files: internal/models/registry.go, internal/server/server.go
  • Problem: Background goroutines update s.registry while concurrent requests access r.FindModel().
  • Action Items:
    1. Add sync.RWMutex to ModelRegistry struct in registry.go.
    2. Wrap FindModel() methods with r.mu.RLock() and r.mu.RUnlock().
    3. Alternatively, implement atomic.Pointer[models.ModelRegistry] in Server to enable lock-free atomic pointer swaps on registry refresh.

Task 2.2: Synchronize Router.Reload()

  • Target File: internal/router/router.go
  • Problem: Router.Reload() replaces r.groups map without holding a mutex lock during active routing requests.
  • Action Items:
    1. Add mu sync.RWMutex to Router struct in router.go.
    2. Acquire r.mu.RLock() during Route(), RouteToConcrete(), and IsGroup().
    3. Acquire r.mu.Lock() during Reload().

Phase 3: Reliability & Stream Fault Tolerance

Task 3.1: Enable Circuit Breaker Protection for Streaming Endpoints

  • Target File: internal/providers/circuit_breaker.go
  • Problem: ChatCompletionStream and ResponsesStream bypass gobreaker entirely.
  • Action Items:
    1. Wrap the initial ChatCompletionStream call inside cb.Execute().
    2. Implement stream response wrapper that monitors streaming errors and reports failure back to the circuit breaker state tracker.

Task 3.2: Prevent Goroutine Leaks on Client Disconnect


Phase 4: Error Handling & Security Hygiene

Task 4.1: Fix Resty Response Body Draining in Error Handlers

  • Target Files: internal/providers/gemini.go, deepseek.go, xiaomi.go
  • Problem: Code attempts io.ReadAll(resp.RawBody()) on consumed bodies.
  • Action Items:
    1. Replace io.ReadAll(resp.RawBody()) with resp.Body() or resp.String().

Task 4.2: Handle Database Error Returns

Task 4.3: Mask Auth Tokens in System Settings API

  • Target File: internal/server/system.go
  • Problem: /api/system/settings returns unmasked auth_tokens.
  • Action Items:
    1. Mask static API tokens before returning in JSON output (e.g. sk-***1234).

Verification & Validation Plan

  1. Unit Testing: Run go test -v -race ./... to verify zero data races.
  2. Integration Verification: Run test completions across all providers (openai, gemini, deepseek, grok, ollama) to confirm streaming and non-streaming responses work correctly.
  3. Security Audit: Verify log output contains no plain-text API keys or tokens.