IA · Ingeniería · MLOps

Auditoría de Producción — AgentFlow

📦 Next.js 14 + Supabase + Stripe 🌿 branch: release/v1.0 📅 18 jun 2026 👤 Auditor: CULTIVA IA
62/100
Arriesgado
No lanzar sin fixes críticos
Production audit: 62/100, arriesgado — el webhook de Stripe no es idempotente (puede duplicar suscripciones), SUPABASE_SERVICE_ROLE_KEY se filtra al cliente, y las rutas /api/admin carecen de auth middleware antes del lanzamiento público.
Bloqueantes — No lanzar hasta resolver
Blocker
Webhook Stripe no idempotente — duplica suscripciones
checkout.session.completed hace un INSERT sin verificar si la suscripción ya existe. Stripe reintenta webhooks fallidos hasta 72h: un segundo intento crea una segunda fila activa, cobrando al usuario sin concederle acceso extra. Puede corromper el billing de todos los usuarios de lanzamiento.
api/stripe/webhook.ts:14
// ❌ AHORA — duplica en retry await supabase.from('subscriptions').insert({ workspace_id, status: 'active' }); // ✅ FIX — upsert idempotente con stripe_subscription_id como clave await supabase.from('subscriptions').upsert( { workspace_id, stripe_subscription_id, status: 'active' }, { onConflict: 'stripe_subscription_id' } );
Blocker
SUPABASE_SERVICE_ROLE_KEY expuesto en componentes client-side
La clave SERVICE_ROLE_KEY (sin prefijo NEXT_PUBLIC_) aparece referenciada en 2 componentes dentro de app/. Next.js la bundleará en el JS del cliente, exponiendo acceso root a Supabase a cualquier visitante. Esto bypassea todas las RLS policies.
.env.example — SUPABASE_SERVICE_ROLE_KEY (verificar app/components/*.tsx)
// Buscar y eliminar del bundle cliente: grep -r "SUPABASE_SERVICE_ROLE_KEY" app/ components/ // Solo debe aparecer en: app/api/**, lib/supabase/server.ts
Blocker
Rutas /api/admin/* sin auth middleware
El matcher en middleware.ts protege /dashboard y /api/agents, pero omite /api/admin/:path*. Cualquier usuario anónimo puede acceder a los endpoints de administración sin autenticación. Con el lanzamiento público, esto es un vector de explotación inmediato.
middleware.ts:3
// ❌ AHORA export const config = { matcher: ['/dashboard/:path*', '/api/agents/:path*'] }; // ✅ FIX — añadir rutas admin export const config = { matcher: [ '/dashboard/:path*', '/api/agents/:path*', '/api/admin/:path*', ] };
Blocker
Migración 20260616 sin índice ni plan de rollback
ALTER TABLE agent_logs ADD COLUMN execution_metadata jsonb se ejecuta en una tabla proyectada en 100k+ filas sin índice en workspace_id. Queries de listado serán full-scans. Además no hay instrucciones de rollback: si falla el despliegue, no se puede revertir la base de datos.
lib/supabase/migrations/20260616_agent_logs.sql
-- ✅ FIX — añadir al final de la migración: CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_agent_logs_workspace ON agent_logs(workspace_id); -- ✅ rollback script (20260616_agent_logs_rollback.sql): ALTER TABLE agent_logs DROP COLUMN IF EXISTS execution_metadata; DROP INDEX CONCURRENTLY IF EXISTS idx_agent_logs_workspace;
Fixes de Alto Valor — Mejoran el score antes del launch
Alta
Sin tests E2E en el path crítico de conversión
CI verde con 47 unit tests al 31% de cobertura. El flujo completo registro → onboarding → pago → activación de agente no está cubierto por ningún test E2E. Un regresión aquí es silenciosa hasta que un cliente real lo reporta. Implementar 1 test Playwright que cubra este flujo antes del lanzamiento.
tests/e2e/ — directorio vacío
Alta
Health check no verifica dependencias externas
/api/health existe pero solo retorna {"status":"ok"}. No verifica conexión a Supabase, Stripe reachability, ni cola de jobs de pg_cron. En producción, si Supabase cae, el health check seguirá verde y el load balancer no drenará el tráfico.
api/health/route.ts
// ✅ FIX — health check con dependencias: const checks = await Promise.allSettled([ supabase.from('workspaces').select('count').limit(1), stripe.balance.retrieve(), ]); return Response.json({ db: checks[0].status, stripe: checks[1].status });
Alta
Variables de entorno sin validación al startup
Si STRIPE_WEBHOOK_SECRET o SUPABASE_SERVICE_ROLE_KEY están vacías en producción, la app arranca pero falla silenciosamente en runtime. Añadir validación con Zod o @t3-oss/env-nextjs que crashee en build time si faltan variables requeridas.
.env.example — 8 variables, 0 validadas
Fortalezas Confirmadas
OK
Firma de webhook Stripe verificada correctamente
stripe.webhooks.constructEvent() se ejecuta antes de procesar cualquier payload. No hay path de bypass. La separación test/live del commit h7i8j0e está correctamente aplicada.
OK
RLS gap en agent_logs corregido (commit d2e3f6a)
El gap de política RLS en agent_logs fue identificado y corregido antes de este audit. Los logs de agentes están correctamente aislados por workspace_id.
OK
CI verde con 47 tests unitarios
GitHub Actions pasa limpio. La lógica core de ejecución de agentes tiene cobertura de unit tests. Base sólida para añadir E2E sobre ella.