strings.Repeat("1", form.Size) — siempre genera "11111...", nunca aleatorio. Vulnerabilidad de fuerza bruta trivial.🔬 Auditoría Estructural — ALUS Codebase
Análisis forense de 3,566 archivos · 20,860 nodos · 28,924 edges · 57 comunidades
📊 Resumen de Hallazgos por Categoría
| Categoría | Críticos | Altos | Medios |
|---|---|---|---|
| 🔐 Seguridad | 3 | 2 | 1 |
| ⚡ Confiabilidad (HTTP/Timeouts) | 2 | 4 | 1 |
| 🏗 Estabilidad (Panics/Races) | 2 | 3 | 1 |
| 📡 Observabilidad | 0 | 2 | 3 |
| 🔄 CI/CD & DevOps | 0 | 1 | 2 |
🔴 Top 7 Issues Bloqueantes para 1M Usuarios
rateLimiter queda nil. La siguiente request hace rateLimiter.isKeyAllowed() → PANIC (nil pointer dereference).restarLitmit permite solo 1 timer a la vez. El mapa userRequests crece sin límite (memory leak). Tras 1 minuto solo resetea 1 entrada. Inútil a escala.⚠ Issues por Microservicio
🏛 archetype-1 (Gateway)
| ID | Archivo:Línea | Severidad | Descripción |
|---|---|---|---|
| A1 | pkg/service_client/service_client.go | CRÍTICO | http.DefaultClient sin timeout — todas las llamadas inter-MS |
| A2 | pkg/orm/connection.go:52-83 | CRÍTICO | Sin MaxOpenConns/MaxIdleConns/ConnMaxLifetime en MySQL |
| A3 | pkg/api/middlewares/rate_limit_middleware.go:88-96 | CRÍTICO | rateLimiter nil tras init fallido → panic |
| A4 | settings.yaml + settings.dev.yaml | CRÍTICO | Secretos JWT/AES/DSN en texto plano |
| A5 | pkg/api/ext/participant/orders.go:100-125 | ALTO | 15+ type assertions sin safe check (posibles panics) |
| A6 | pkg/api/ext/participant/orders.go | ALTO | SAGA: data race en authEmbed compartido entre pasos |
| A7 | pkg/api/ext/participant/orders.go:300 | ALTO | productOfferedId=10066, userIdentifierTypeId=1 hardcodeados |
| A8 | pkg/api/v1/api.go:44-46 | MEDIO | ReadTimeout=100s, WriteTimeout=300s — susceptible slow-loris |
| A9 | pkg/service_client/externalapi/externalapi.go:168-177 | ALTO | Reflect para auth fields — fragile, panics on nil |
| A10 | pkg/service_client/externalapi/externalapi.go:392-447 | ALTO | PII en logs — request/response bodies completos |
| A11 | pkg/api/v1/api.go | ALTO | CORS: allow_origin "*" en producción |
✅ validation-v2
| ID | Archivo:Línea | Severidad | Descripción |
|---|---|---|---|
| V1 | app/service/otp_code.go:69 | CRÍTICO | OTP = strings.Repeat("1", n) — nunca aleatorio |
| V2 | app/pkg/middlewares/rate_limit.go:14 | CRÍTICO | restarLitmit global: solo 1 timer, memory leak en userRequests |
| V3 | app/pkg/middlewares/rate_limit.go:24 | MEDIO | Límite hardcodeado >=5, no configurable |
📦 catalog-service
| ID | Archivo:Línea | Severidad | Descripción |
|---|---|---|---|
| C1 | pkg/api/v2/clients/catalog.go:1539,1631 | CRÍTICO | Emails personales hardcodeados |
| C2 | pkg/api/v2/clients/catalog.go:1541-1542 | ALTO | go Mailing.ProductSoldOut() sin recover — posible panic huérfano |
| C3 | pkg/api/v2/clients/catalog.go:68 | ALTO | context.Get(r,"Client").(AuthClient) — panic si key ausente |
| C4 | pkg/api/v2/clients/catalog.go | ALTO | N+1 queries: múltiples loops llamando DB por producto |
| C5 | settings.yaml:17,23,44,48 | CRÍTICO | access_secret, password, tokens en texto plano |
👥 users-management
| ID | Archivo:Línea | Severidad | Descripción |
|---|---|---|---|
| U1 | app/repository/gorm.go:27 | ALTO | RaiseErrorOnNotFound=true en Statement compartido — race entre goroutines |
| U2 | app/repository/gorm.go | MEDIO | CountWhere silencia errores — retorna 0 sin loguear |
| U3 | settings.sample.yaml:24,30,38,43,51 | CRÍTICO | Secrets en texto plano: access_secret, password, tokens |
🔄 CI/CD & DevOps
| ID | Archivo | Severidad | Descripción |
|---|---|---|---|
| D1 | Todos los .gitlab-ci.yml | ALTO | Sin SAST, secret scanning, dependency scanning, Trivy |
| D2 | Dockerfile (single-stage) | ALTO | Ejecutan como root, COPY . . copia secrets al image layer |
| D3 | Todos los .gitlab-ci.yml | MEDIO | "E2E tests" = sleep 45 + curl /status — no son tests reales |
| D4 | Todos los Dockerfiles | MEDIO | Sin HEALTHCHECK en ningún contenedor |
| D5 | .gitlab-ci.yml | MEDIO | Sin coverage gate — tests pueden fallar silenciosamente |
🔄 Flujos End-to-End Analizados
Flujo 1: Redención de Puntos (Purchase Order) — El más crítico
Flujo 2: Autenticación Externa (BBVA Credits)
Flujo 3: Generación OTP (Validación de Identidad)
📋 Propuestas — Con Restricción de 5 Semanas
⏰ La Restricción: 5 Semanas
Toda decisión arquitectónica debe evaluarse contra esta ventana de tiempo. Una solución perfecta que toma 8 semanas es un fracaso. Una solución suficiente en 5 semanas es una victoria.
📊 Matriz Esfuerzo vs Escalabilidad
3 días · +5x throughput"] A2["SQLX + Redis
5 días · +8x throughput"] A3["KrakenD Edge
2 días · +3x throughput"] A4["Circuit Breaker
1 día · Previene fallos"] A5["ProxySQL + Réplicas
1 día · +5x reads"] A6["RabbitMQ SAGA
4 días · +20x writes"] end subgraph "⚠ Riesgoso en 5 semanas" B1["DOKS Migración
5-8 días · Alto riesgo"] B2["Kafka Cluster
15-20 días · Muy alto riesgo"] B3["CQRS Completo
20-30 días · Imposible"] end subgraph "❌ Fuera de plazo" C1["Kafka + Event Sourcing
12-20 semanas"] C2["Multi-Region
8-12 semanas"] C3["Reescritura total
20-30 semanas"] end style A1 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style A2 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style A3 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style A4 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style A5 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style A6 fill:#14532d,stroke:#22c55e,color:#bbf7d0 style B1 fill:#451a03,stroke:#f59e0b,color:#fcd34d style B2 fill:#451a03,stroke:#f59e0b,color:#fcd34d style B3 fill:#450a0a,stroke:#ef4444,color:#fca5a5 style C1 fill:#450a0a,stroke:#ef4444,color:#fca5a5 style C2 fill:#450a0a,stroke:#ef4444,color:#fca5a5 style C3 fill:#450a0a,stroke:#ef4444,color:#fca5a5
🎯 Opción 1: Mínimo Viable (Semanas 1-2, Equipo de 2 devs)
TTM: 2 semanas · Escala a: ~100K usuarios · Riesgo: Bajo · Costo: +$200/mes
Solo arreglar los 7 issues críticos + SQLX en 3 queries. Sin nueva infraestructura. Sin cambios de arquitectura.
| Qué incluye | Esfuerzo | Qué NO incluye |
|---|---|---|
| ✅ OTP criptográfico | 30 min | ❌ KrakenD |
| ✅ HTTP timeouts (5s) | 2h | ❌ Redis cache |
| ✅ Pool MySQL configurado | 1h | ❌ RabbitMQ |
| ✅ Rate limiters arreglados | 3h | ❌ Read replicas |
| ✅ Secrets → env vars | 4h | ❌ Circuit breaker |
| ✅ SQLX en top 3 queries | 1 día | ❌ Frontend changes |
| Total: | ~3 días |
⚠ Limitación: A 500K usuarios, el SAGA síncrono vuelve a ser cuello de botella. Sirve para salir a producción con Clip rápido, pero requiere Opción 2 en 1-2 meses.
⭐ Opción 2: Recomendada (Semanas 1-5, Equipo de 3-4 devs + IA)
TTM: 5 semanas · Escala a: ~1M usuarios · Riesgo: Medio · Costo: +$530/mes
Estabilización + Optimización + Desacoplamiento asíncrono del SAGA. Todo lo que escala sin romper el plazo.
| Semana | Qué se hace | Esfuerzo | Escala a |
|---|---|---|---|
| 1-2 | 7 críticos + SQLX top 10 + Redis cache + KrakenD | 8 días | ~200K |
| 3 | Circuit breaker + ProxySQL + Read Replicas + Fix N+1 | 4 días | ~500K |
| 4 | RabbitMQ/Redis Streams + Workers async (SAGA) | 4 días | ~800K |
| 5 | Frontend polling + Schema Clients MS + Load Testing k6 | 5 días | ~1M |
✅ Ventaja decisiva: Cada semana agrega capacidad incremental. Si la semana 5 se complica, la semana 4 ya soporta 800K usuarios — suficiente para el lanzamiento de Clip.
🚀 Opción 3: Máxima Ambición (Semanas 1-5, Equipo de 5-6 devs + IA intensiva)
TTM: 5 semanas (alta presión) · Escala a: ~2M usuarios · Riesgo: Alto · Costo: +$800/mes
Todo lo de Opción 2 + DOKS (Kubernetes) + OpenTelemetry. Solo si hay 5+ devs disponibles.
| Extra sobre Opción 2 | Esfuerzo | Riesgo |
|---|---|---|
| DOKS (Kubernetes) + HPA + Helm charts | 3 días | Alto — posibilidad de errores de configuración K8s bajo presión |
| OpenTelemetry tracing entre MS | 2 días | Bajo |
| CI/CD con SAST + secret scanning | 1 día | Bajo |
| Docker multi-stage para todos los MS | 1 día | Bajo |
⚠ Solo intentar si: El equipo ya tiene experiencia con Kubernetes en producción. Si es el primer proyecto K8s del equipo, descartar esta opción para el plazo de 5 semanas.
❌ Opción DESCARTADA: Kafka + Event Sourcing + CQRS
TTM real: 12-20 semanas · Costo: +$2,500/mes · Riesgo: Extremo
Kafka requiere: aprendizaje del equipo, configuración de clúster, gestión de tópicos/particiones, manejo de offsets, schema registry, exactly-once semantics. Imposible en 5 semanas. Evaluar como Fase 2 (Q3 2026) si Clip supera 1M usuarios sostenidos.
🔥 Recomendación Final — Con Restricción de Tiempo
| Criterio | Opción 1 | Opción 2 ⭐ | Opción 3 |
|---|---|---|---|
| TTM | 2 sem | 5 sem | 5 sem (presión) |
| Usuarios soportados | ~100K | ~1M | ~2M |
| Riesgo de no entregar | 5% | 15% | 40% |
| Devs necesarios | 2 | 3-4 | 5-6 |
| Costo mensual extra | +$200 | +$530 | +$800 |
| Deuda técnica residual | Alta | Media | Baja |
| Requiere rework en 3 meses | Sí | No | No |
→ Ejecutar Opción 2.
Semanas 1-2: Estabilización — Sin esto, nada funciona.
Semanas 3-4: Optimización + Async — El SAGA deja de ser síncrono.
Semana 5: QA + Load Test + Release — Solo ajustes finos.
Regla de oro: Cada viernes debe haber una versión desplegable. Si algo se atrasa, se corta scope, no calidad. Mejor entregar 800K estables que 2M rotos.
NO fork para Clip. Trunk-based + Feature Flags. Las correcciones benefician a todos los tenants.
🗓 Roadmap de 5 Semanas
🤖 Estrategia de Aceleración con IA
| Tarea | Cómo la IA acelera | Factor |
|---|---|---|
| GORM → SQLX | Traduce queries automáticamente. Tests generados. | 3x más rápido |
| KrakenD config | Genera krakend.json desde rutas actuales del Gateway | 5x |
| Circuit breaker | Genera wrappers HTTP con gobreaker para cada MS client | 4x |
| Schema Clients MS | Migraciones SQL + DAOs desde el JSON actual | 3x |
| OpenTelemetry | Instrumentación automática de handlers Gin/Gorilla | 5x |
| k6 load tests | Scripts de carga simulando flujos SAGA completos | 4x |
🧪 Simulador de Carga — Actual vs Propuesto
🏗 Actual
🔧 Camino A+B
⚡ Camino A+B+C
📊 Throughput Comparativo
💰 Costo Mensual DO
| Recurso | Actual | Camino A+B | Camino A+B+C |
|---|---|---|---|
| Droplets/DOKS | $96 | $192 | $288 |
| Load Balancer | - | $20 | $20 |
| MySQL Managed | - | $120 | $120 |
| Redis Managed | - | $60 | $60 |
| RabbitMQ | - | - | $40 |
| TOTAL | $96 | $392 | $528 |