CreaRack-SL

Prescan explícito de Windows Defender ANTES del swap (ADR: cerrar s201 · cierre diagnóstico auto-update)

Contexto

El auto-update del Agente sufría fallos aleatorios no-deterministas que NO se producían con instalación manual del mismo binario. Diagnóstico s201 (activo desde 03-07-2026).

Carrera crítica: El instalador del navegador tarda minutos entre descargar el .exe y ejecutarlo (el usuario hace click). En ese tiempo, Windows Defender completa su análisis cloud. El auto-update descarga y ejecuta en segundos → el análisis está a medias → MpCmdRun (protección cloud de Defender) retiene la extracción del onefile del binario congelando el arranque sin matarlo.

Observación del watchdog de 2.13.4: Detectó “process alive but not responding” tras 30 segundos y decidió: “debe estar roto, killamos y hacemos rollback”. Pero el proceso NO estaba roto, estaba retenido. Resultado: rollback de un binario funcional.

Decisión

Prescan proactivo de Defender ANTES del swap (nuevo en 2.13.5):

  1. En _apply_swap(), antes de _selftest_new_exe(), llamar a _defender_prescan(NEW_EXE)
  2. _defender_prescan ejecuta MpCmdRun -Scan -ScanType 3 -File <path> (síncrono, 180 s timeout)
  3. Al terminar, el análisis es “conocido” y el primer arranque post-swap behave como el de la instalación manual
  4. Best-effort: sin MpCmdRun (Linux CI, otro AV), se salta con traza

Rationale

  • Replicar el tiempo “muerto” del flujo manual: 30-60 segundos de análisis pre-ejecución
  • Más simple que esperar pasivamente: no podemos pedir al usuario que espere; el prescan los hace al Agente (la máquina que se actualiza)
  • No depende de certificados: Windows Defender es gratis + built-in en Windows 10/11
  • Alineación con watchdog paciente (2.13.5): el sondeo de 10 s cada 150 s da tiempo al prescan + análisis + arranque inicial del binario nuevo

Alternativas consideradas (y rechazadas)

AlternativaMotivo del rechazo
Esperar 60 s entre descarga y swapDejaría el Agente sin servicio 60 s innecesariamente; el prescan es más eficiente
Bajar la confianza del certificado de código (pagar)Costo O(100 €/año); Defender es gratis
Habilitar análisis offline (descarga local de sigs)O(150 MB) en el onefile; sin garantías offline en máquinas con blue screen
Usar VirusTotal o similarDependencia externa + latencia + límites de rate; incompatible con aire cerrado

Implementación

Archivo: terminal/agent/core/updater.py

def _defender_prescan(path) -> None:
    """Fuerza el análisis de Defender ANTES del primer arranque post-swap."""
    import subprocess
    
    mpcmdrun = os.path.join(os.environ.get("PROGRAMFILES", r"C:\Program Files"),
                            "Windows Defender", "MpCmdRun.exe")
    if not os.path.exists(mpcmdrun):
        _wlog("prescan Defender omitido (MpCmdRun no encontrado)")
        return
    
    try:
        proc = subprocess.run(
            [mpcmdrun, "-Scan", "-ScanType", "3", "-File", str(path)],
            capture_output=True,
            timeout=180,
            creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
        )
        # rc 0 = limpio · rc 2 = amenaza (self-test posterior fallará → swap abortado)
        _wlog(f"prescan Defender del staged: rc={proc.returncode}")
    except Exception as e:
        _wlog(f"prescan Defender fallo (no bloqueante): {type(e).__name__}: {e}")

Invocado en _apply_swap() antes del self-test.

Verificación

3 tests en tests/agent/test_agent_updater_trace.py:

  1. Watchdog contiene WaitHealthy y pollea cada 10 s (regresión contra 2.13.4)
  2. Prescan se salta sin MpCmdRun (CI + otro AV)
  3. version.txt se reconcilia tras rollback

Impacto

  • ✅ Cierre de s201: primer rescate autónomo del watchdog se recupera sin revertir versiones buenas
  • ✅ Instalación manual y auto-update ahora comparten el mismo “tiempo de análisis”
  • ✅ Sin impacto en máquinas sin Defender (se salta gracefully)
  • ✅ Sin cambios de UI, help, o documentación pública

Véase también

  • [[feature—terminal—auto-update-self-healing-2-13-5]]
  • [[feature—terminal—traza-persistente-swap-auto-update]]
  • [[concept—general—como-funciona-el-mecanismo-de-auto-update-del-local-agent]]
  • [[incident—20260705—s201-auto-update-falla-aleatorio]]