CreaRack-SL

Feature s104 · Auditoría Suprema — 4 arreglos de seguridad en el núcleo

Visión general

Primera tanda express de correcciones salida de la Auditoría Suprema (s103), una revisión exhaustiva del código de CreaRack Pro enfocada en seguridad de permisos y aislamiento entre clientes (multi-tenancy).

Se cerraron 4 hallazgos ALTA confirmados con verificación adversarial, todos en el dominio core. El riesgo real hoy es bajo (solo accede el equipo, sin clientes externos aún), pero se cierran antes de abrir a clientes.

Fecha: 2026-06-02 (Sesión 104) | Autor: Edu (Code Opus) | Ámbito: Seguridad · permisos y aislamiento

Los 4 hallazgos ALTA

1. Descifrado de credenciales sin guard de rol

  • Función afectada: core.credential_api::decrypt_credential (GET /api/credentials/{id}/decrypt)
  • Riesgo: Un usuario readonly podía extraer y ver en claro las contraseñas SSH/SNMP/HTTP guardadas (secretos del cliente).
  • Fix: Reforzar guard a has_permission(request.user, "network", "edit") (operador+). Un readonly ya no puede.
  • Líneas: core/credential_api.py:247 (+4 LOC guard, +3 LOC docstring).
  • Test: tests/api/test_credentials.py::test_decrypt_as_readonly_rejected y test_decrypt_as_operator_allowed.

2. Edición de organización sin guard de rol

  • Función afectada: core.api.settings::update_company_settings (PUT /api/settings/company)
  • Riesgo: Un usuario readonly podía renombrar/editar la organización sin restricción.
  • Fix: Guard explícito: if not is_admin(request.user): raise HttpError(403, "Admin access required").
  • Líneas: core/api/settings.py:29 (+3 LOC guard).
  • Test: tests/api/test_settings.py::test_update_company_as_readonly_rejected y test_update_company_as_admin_allowed.

3. Fail-open silencioso en evaluador de permisos

  • Función afectada: core.utils.permissions::has_permission (evaluador multi-capa)
  • Riesgo: Los bloques de TemporaryAccess y ModulePermission tragaban toda excepción (except Exception: pass), enmascarando fallos BD inesperados. Ante un error puntual de BD, el usuario caía a los defaults del rol: potencialmente MÁS permisivo que una denegación explícita → fail-open.
  • Fix: Estrechar el except a ProgrammingError / OperationalError (caso “tabla no migrada” en deployments iniciales, silencioso). Cualquier otra excepción se registra con logger.exception(...), haciendo visible el fallo.
  • Líneas: core/utils/permissions.py:91 y 114 (+14 LOC, comentarios + logging).
  • Test: tests/test_permissions.py::test_unexpected_error_is_logged_not_swallowed y test_migration_error_stays_silent.

4. Bypass de aislamiento de tenants en alertas del Agent

  • Función afectada: terminal.api.sentinel::receive_agent_alert (POST /api/agent/alert)
  • Riesgo: El endpoint confiaba en el tenant_id del body JSON en vez del JWT firmado. Un Agent con token de tenant A podía escribir alertas sobre targets de tenant B → RLS bypass.
  • Fix: Validar agent_data["tenant_id"] == payload.tenant_id (como en rutas hermanas). Usar JWT tenant para filtrar targets.
  • Líneas: terminal/api/sentinel.py:129 (+7 LOC guard + comentario).
  • Test: tests/api/test_sentinel.py::test_alert_rejects_body_tenant_spoofing y test_alert_matching_tenant_passes_guard.

Tests añadidos

  • tests/api/test_credentials.py — 2 tests nuevos (decrypt readonly/operator).
  • tests/api/test_settings.py — 2 tests nuevos (update readonly/admin).
  • tests/api/test_sentinel.py — 2 tests nuevos (tenant spoofing/matching).
  • tests/test_permissions.py — 2 tests nuevos (fail-open, migration error).

Total: 8 tests verde (todos pasan, pre-commit limpio, ruff ok).

Bug preexistente detectado

Durante la auditoría se detectó un 5º ALTA (fuera de alcance s104):

  • Ubicación: core/middleware/admin_paths.py
  • Riesgo: Bypass de /admin por spoofing de X-Forwarded-For. Requiere verificar la topología Traefik en PROD.
  • Acción: Anotado para tratamiento separado (requiere cambios en infra).

Además, se encontró un bug preexistente en receive_agent_alert: construye MonitoringAlert con kwargs inexistentes (alert_type/message) → happy path siempre 500. El endpoint de alertas del Agent nunca ha creado una alerta correctamente. Anotado para backlog.

Deuda técnica reconocida

El archivo sentinel.py ronda 525 LOC lógicas (>500, límite de Regla 5). Ya estaba en 522 antes de este fix. El troceo está anotado como deuda de modularización de la auditoría (out of scope s104). El [--no-verify] fue autorizado por Edu.

Documentación

  • CHANGELOG.md — entrada s104 con detalles de cada fix.
  • RELEASE_NOTES.md — entrada s104 con resumen de impacto al usuario.

Véase también

  • [[entity—core—service—decrypt-credential]]
  • [[entity—core—endpoint—update-company-settings]]
  • [[entity—core—service—has-permission]]
  • [[entity—terminal—endpoint—receive-agent-alert]]
  • [[decision—20260602—auditoria-suprema-4-hallazgos-alta]]
  • [[concept—security—fail-open]]
  • [[concept—saas—multi-tenancy]]
  • [[concept—security—role-based-access-control]]