Volver a la wiki

Auditoría Suprema Terminal — Fase A Backend: Correctness/Honestidad

Resumen

Tanda de correcciones internas del backend del Agente Terminal (sesión s123, 2026-06-10). Fixes de honestidad y robustez en cuatro áreas clave:

  1. Endpoint /api/agent/alert reparado — dejaba de devolver HTTP 500 perpetuo.
  2. Promoción de Primary atómica — evita inconsistencias en la flota.
  3. Endurecimiento de autenticación — refresh tokens rechazados para peticiones de datos.
  4. Retiro de código muerto — módulo terminal/api/scripts.py eliminado.

Detalles por área

1. Alerta de Agente (receive_agent_alert)

Archivo: terminal/api/sentinel.py::receive_agent_alert, terminal/consumers.py::_handle_alert

Problema: Ambos endpoints intentaban crear un MonitoringAlert (modelo de configuración de reglas) con campos que no existen en el esquema:

Resultado: TypeError no capturado → HTTP 500 siempre, alerta perdida silenciosamente (Reglas 13/15 de honestidad).

Comprobación: El Agente actual nunca invoca este canal (send_alert existe en el código pero no se llama). Las alertas reales se derivan en el SaaS de las métricas que el Agente empuja (ver monitoring.api.evaluate_and_trigger_alerts).

Fix: Ambos receptores ahora registran un log de advertencia y responden honestamente:

Nota: Si en el futuro el Agente emite alertas propias, se enrutarán por el motor real de monitorización, no por este extremo.

2. Promoción de Primary (promote_agent)

Archivo: terminal/api/fleet.py::promote_agent

Problema: La secuencia demote (Primary anterior) + promote (nuevo) se ejecutaba sin transacción:

# ANTES (sin transacción):
current_primary.role = "secondary"
current_primary.save()

agent.role = "primary"
agent.save()

Si dos requests de promoción llegaban concurrentemente, la ventana entre reads y writes podía dejar:

Fix: Serialización con transaction.atomic() + select_for_update():

with transaction.atomic():
    agent = AgentInstance.objects.select_for_update().filter(...).first()
    current_primary = AgentInstance.objects.select_for_update().filter(...).first()
    # demote + promote bajo el lock
    ...
# Notificaciones WS fuera del lock (no retener la transacción durante I/O de red)
_send_role_change_bg(current_primary.agent_id, "secondary")
_send_role_change_bg(agent.agent_id, "primary")

Ahora la reasignación es atómica dentro de la organización.

3. Endurecimiento de Autenticación

Archivo: terminal/api/auth.py::get_agent_from_request

Problema: Un refresh token (larga vida, 180 días) podía autenticar peticiones de datos del Agent.

# ANTES:
payload = verify_agent_token(token)
return payload  # sin revisar tipo

Un refresh token tiene type="refresh" en su payload. Un agente malintencionado con acceso a un refresh token podía usarlo para solicitar datos hasta 180 días después (vuln SA2 BAJA).

Fix: Ahora verifica explícitamente type="access":

payload = verify_agent_token(token)
if not payload or payload.get("type") != "access":
    return None
return payload

Access tokens (corta vida, ~15 min) son los únicos válidos. Los genera generate_agent_token().

4. Código Muerto: terminal/api/scripts.py

Archivo: Eliminado terminal/api/scripts.py + su re-exportación en __init__.py

Contexto: El router de Scripts SSH estaba expuesto en terminal/api/__init__.py pero nunca se montaba en las URLs. El punto de entrada real es network.api.scripts_router en /api/scripts.

Impacto: El modelo terminal.models.Script se conserva (lo usan backup/restore). Solo se retira el endpoint fantasma y el módulo redundante.

Cambios Operacionales

Reglas de Honestidad Tocadas

Véase también

Subir