CreaRack-SL

Decisiones de la Auditoría Suprema s105 (Blueprints/Auto-Plan)

Contexto

Sesión 105 de la Auditoría Suprema: revisión exhaustiva de seguridad y correctness del dominio blueprints / Auto-Plan.

Hallazgos: 61 crudos → 40 únicos → 22 confirmados (7 ALTA, 12 MEDIA, 3 BAJA).

Decisión de Edu: “Arreglar todo de una vez. Definitivo. No se parceliza.”


Decisión 1: Arreglar todos los 22 hallazgos en UNA sola PR

Discusión: Algunas PRs grandes (>50-100 KB de diff) se pueden parcelar en sub-PRs por dominio o por tipo de problema. Ejemplo:

  • PR1: Permisos + access control.
  • PR2: Cross-tenant + aislamientos.
  • PR3: Correctness (N+1, endpoint roto, etc.).

Razón del NO-parcelizar:

  • Los hallazgos están entrelazados: Broken Access Control + Auto-Plan costoso → necesario bloquear simultáneamente.
  • Prompt global compartido + resemantización de AIPrompt → una sola migración de datos (si parcelizo, tengo dos migraciones conflictivas).
  • Tests nuevos requieren todos los fixes simultáneamente para pasar.
  • Riesgo de regresión: cada sub-PR deja la aplicación en estado inseguro o incompleto (readonly puede seguir llamando Auto-Plan hasta que la PR de permisos se mergee).
  • Versionado: una sola entry en CHANGELOG/RELEASE_NOTES es más limpio y auditable.

Resultado: 1 commit, ~300-400 LOC diff, cambios en 14 archivos. Mergea hoy (2026-06-03), directa a main.


Decisión 2: Dos items quedan para PR distinta (documentado)

Problema: La auditoría identifica otros dos issues del dominio blueprints que sí se parcelizan:

2a. Handler global de str(exc) en config/urls.py

Problema: ~509 endpoints de CreaRack exponen str(e) en los exception handlers (fuga de información interna: stack traces, SQL, rutas, etc.).

Por qué NO se arregla en s105:

  • Cambio transversal a todo el proyecto (no solo blueprints).
  • Side effects desconocidos: algunos endpoints pueden depender de mensajes específicos en tests o cliente.
  • Requiere auditoría de cada handler para decidir error message genérico vs específico.
  • Risk level: MEDIA (vs ALTA en permisos).

Acción: se documenta en RELEASE_NOTES como “fuera de tanda”. PR aparte cuando sea el momento.

2b. Auto-Plan asíncrono (Celery / tarea background)

Problema: magic_import_blueprint() es síncrono, llamada IA bloqueante (puede tardar 5-30s). El Map Editor no tiene UI de spinner/polling.

Por qué NO se arregla en s105:

  • Cambio de arquitectura (no solo seguridad): necesita Celery + Redis queue.
  • Toca frontend (Map Editor JS): requiere UI de spinner + polling / WebSocket.
  • Puede requerir cambios en la DB (tabla de Jobs, status tracking).
  • Risk level: MEDIA (vs ALTA en decompression bomb).
  • Scope: Multi-sprint (s105 = 1 sesión, async = 2-3 sesiones).

Acción: se documenta como “fuera de tanda”. Feature page separada cuando se inicie.

Mención en RELEASE_NOTES: “Conversión de Auto-Plan a asíncrono → PR propio (frontend). Prioridad: media. Tracking: issue #XYZ.”


Decisión 3: Naming del prompt activo

Alternativas consideradas:

  • __brain_active__ (opción elegida)
  • __reserved_active__
  • active (muy ambiguo, conflictaría fácilmente con saved prompts)
  • Un boolean is_active en el modelo (añade lógica, violaría single-name per org)

Razón de __brain_active__:

  • Naming magic (double underscore) es convención Python para privado/reservado.
  • Descriptivo: __brain_active__ → “prompt activo del AI Brain”.
  • Evita colisión: un usuario no llamaría su saved prompt __brain_active__ casualmente.
  • Validación en API: if name == "__brain_active__": return 400.

Decisión 4: Fallback a fichero semilla (no error)

Alternativas:

  • A. Semilla como fallback (opción elegida): si una org no tiene prompt activo, lee prompts/blueprint_analyst.md (read-only).
  • B. Error 500: si no existe prompt activo, falla en Auto-Plan.
  • C. Prompt vacío: si no existe, usa string vacío (riesgo: IA recibe instrucción nula).

Razón de A (fallback):

  • Backward compatibility: orgs nuevas o viejas sin personalización siguen funciona.
  • UX: no confunde al usuario con “prompt no disponible”.
  • Migración suave: puedo hacer rolling migration sin downtime.
  • Fichero read-only garantiza que nunca se corrompe (nadie lo toca).

Decisión 5: Soft-delete de racks en blueprints

Alternativas:

  • A. Hard-delete (antes, endpoint delete_rack_total): borra inmediatamente, irreversible.
  • B. Soft-delete + papelera (opción elegida): marca deleted_at, recuperable.
  • C. “Archive”: move a tabla ArchivedRack (overhead de tamaño).

Razón de B (soft-delete):

  • Consistencia: el resto del producto (racks, blueprints) usa soft-delete.
  • Auditoría: queda trace de qué se borró, cuándo, quién.
  • Recuperación: usuario borra accidentalmente un rack → papelera lo recupera.
  • RLS: filtro deleted_at__isnull=True en todos los listados de edición.

Decisión 6: Limit 25 MB para Auto-Plan

Alternativas:

  • 10 MB (muy restrictivo, rechaza imágenes normales)
  • 25 MB (opción elegida)
  • 100 MB (muy permisivo, DoS)
  • Sin límite (riesgo de abuso)

Razón de 25 MB:

  • Típica: imagen de plano de datacentro A4 a 300dpi ≈ 8-15 MB.
  • Buffer: 25 MB = 1.5x - 2x la típica (edge cases, pantallazos múltiples).
  • Decompression: Pillow puede expandir <25 MB a >64 MP (controlado con guard).
  • IA: Google/Claude API generalmente limitan a 20-30 MB por request.

Decisión 7: Guard de decompression bomb: 64 MP

Alternativas:

  • 16 MP (muy restrictivo)
  • 32 MP (intermedio)
  • 64 MP (opción elegida)
  • 256 MP (muy permisivo, slow)

Razón de 64 MP:

  • Típica: plano de 4000x3000 = 12 MP. Mapa grande 8000x6000 = 48 MP.
  • Ultra-wide: mosaicos o stitched images hasta 64 MP son realistas.
  • Tiempo: procesamiento de 64 MP = segundos, acceptable.
  • Seguridad: rechaza intentos de cuelgue tipo “1 pixel × 2^32 píxeles” expandido a gigabytes.

Follow-ups documentados

  1. PR handler global str(exc): transversal a config/urls.py. Timing: próximas 1-2 sprints.
  2. PR Auto-Plan async: Celery + frontend. Timing: próximas 2-3 sprints. Depende de: definición de formato de job, schema de polling, WebSocket vs HTTP polling.
  3. Prompt migration script (si aplica): script para migrar orgs existentes sin prompt activo (opcional, fallback ya cubre).

Checklist de verificación (s105)

  • 22 fixes arreglados y testeados.
  • 10 tests nuevos en test_blueprints_security.py.
  • require_perm aplicado a ~25 endpoints.
  • AIPrompt resemantizado (BD-backed, per-org).
  • Auto-Plan: límite 25 MB, atomic, audit log, error genérico.
  • Decompression bomb: guard 64 MP.
  • Racks soft-deleted no aparecen en mapas.
  • /placements endpoint reparado.
  • N+1 en /list arreglado.
  • Tests local: 54 verdes, ruff + django check limpios.
  • CHANGELOG + RELEASE_NOTES actualizados.

Véase también

  • [[feature—blueprints—auditoria-s105]]
  • [[entity—core—service—require-perm]]
  • [[entity—blueprints—model—aiprompt]]
  • [[concept—saas—multi-tenancy]]