Maistro.mx - Codebase Constraints & Guardrails
> **Este documento define reglas NO NEGOCIABLES de seguridad y arquitectura.**
Maistro.mx - Codebase Constraints & Guardrails
Este documento define reglas NO NEGOCIABLES de seguridad y arquitectura.
Cualquier PR que viole estas restricciones será rechazado automáticamente.
🔒 Seguridad (Security Constraints)
SC-001: Service Role Key NUNCA en Cliente
Estado: CRÍTICO
Violación: SUPABASE_SERVICE_ROLE_KEY expuesta en bundle cliente
// ❌ PROHIBIDO - En código cliente (src/)
const serviceRoleKey = import.meta.env.SUPABASE_SERVICE_ROLE_KEY;
const supabase = createClient(url, serviceRole_KEY); // Bypass RLS!
// ✅ CORRECTO - Solo Anon Key en cliente
const supabase = createClient(url, anonKey); // Respeta RLS
// ✅ CORRECTO - Service Role solo en Edge Functions
const supabase = createClient(url, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY"));
Verificación:
npm run build
grep -r "SERVICE_ROLE" dist/ && echo "❌ FALLÓ" || echo "✅ PASS"
SC-002: Webhooks SIEMPRE Validan Firma
Estado: CRÍTICO
Violación: Aceptar webhooks sin validar x-signature o stripe-signature
// ❌ PROHIBIDO
serve(async (req) => {
const body = await req.json(); // Sin validación!
processPayment(body);
});
// ✅ CORRECTO - MercadoPago
const signature = req.headers.get("x-signature");
const isValid = await verifyMpSignature(signature, body, secret);
if (!isValid) return new Response("Invalid", { status: 401 });
// ✅ CORRECTO - Stripe
const event = await stripe.webhooks.constructEventAsync(body, signature, secret);
Verificación:
curl -X POST https://<project>.supabase.co/functions/v1/webhook-mercadopago \
-d '{"type":"payment"}'
# Debe retornar 401 (sin firma)
SC-003: CORS Wildcard PROHIBIDO en Pagos
Estado: ALTO
Violación: Access-Control-Allow-Origin: * en funciones de pago
// ❌ PROHIBIDO
const corsHeaders = {
"Access-Control-Allow-Origin": "*", // Cualquier sitio puede llamar
};
// ✅ CORRECTO
const ALLOWED_ORIGINS = [
"https://maistro.mx",
"https://www.maistro.mx",
"http://localhost:5173", // Solo dev
];
const corsHeaders = {
"Access-Control-Allow-Origin": ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0],
};
SC-004: Sanitización Obligatoria para HTML
Estado: ALTO
Violación: dangerouslySetInnerHTML sin sanitizar
// ❌ PROHIBIDO
<div dangerouslySetInnerHTML={{ __html: userContent }} />
// ✅ CORRECTO
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userContent) }} />
💰 Financiero (Financial Constraints)
FC-001: Actualizaciones de Wallet DEBEN ser Atómicas
Estado: CRÍTICO
Violación: Read-Modify-Write en memoria
// ❌ PROHIBIDO - Race condition
const { data: wallet } = await supabase.from("wallets").select("balance").single();
const newBalance = wallet.balance + amount; // Otro proceso puede haber cambiado esto
await supabase.from("wallets").update({ balance: newBalance });
// ✅ CORRECTO - Usar función atómica RPC
await supabase.rpc("atomic_wallet_credit", {
p_transaction_id: transactionId,
p_wallet_id: walletId,
p_amount: amount,
});
// ✅ CORRECTO - SQL atómico directo
await supabase.rpc("atomic_wallet_credit", { p_wallet_id, p_amount });
Función SQL Requerida:
UPDATE maistro_wallets
SET balance = balance + p_amount
WHERE id = p_wallet_id;
FC-002: Idempotencia Obligatoria en Pagos
Estado: ALTO
Violación: Procesar el mismo pago/webhook múltiples veces
// ✅ CORRECTO - Verificar antes de procesar
if (await isEventProcessed(supabase, eventId)) {
return { received: true, processed: false, reason: "already_processed" };
}
// Procesar pago...
// Marcar como procesado
await markEventProcessed(supabase, eventId, eventType);
Tabla Requerida:
CREATE TABLE maistro_processed_webhooks (
provider VARCHAR(50),
event_id VARCHAR(255),
processed_at TIMESTAMPTZ,
UNIQUE(provider, event_id)
);
🌐 API & Rate Limiting
API-001: Rate Limiting en Operaciones Sensibles
Estado: MEDIO
Aplica a: Chat, pagos, creación de jobs
// ✅ CORRECTO - Verificar rate limit antes de operar
if (!await checkRateLimit(userId, 'send_message', 30, 1)) {
throw new Error('Rate limit exceeded: Max 30 messages per minute');
}
Límites Actuales:
| Operación | Límite | Ventana |
|---|---|---|
send_message | 30 | 1 minuto |
create_job | 10 | 1 hora |
purchase_lead | 20 | 1 hora |
wallet_credit | 5 | 1 minuto |
🔐 Secrets & Configuración
SEC-001: Naming Convention para Env Vars
Estado: OBLIGATORIO
| Prefijo | Uso | Ejemplo |
|---|---|---|
VITE_* | Variables accesibles en browser | VITE_SUPABASE_URL |
| Sin prefijo | Server-only (Edge Functions) | SUPABASE_SERVICE_ROLE_KEY |
STRIPE_* | Server-only Stripe | STRIPE_SECRET_KEY |
Reglas:
- NUNCA usar
VITE_para secrets server-only - NUNCA commit
.env.local(está en.gitignore) - SIEMPRE usar
.env.examplecomo template
🧪 CI/CD Guardrails
CI-001: Audit Harness Obligatorio
Estado: OBLIGATORIO
Cada deploy debe pasar:
# En pipeline CI/CD
pwsh .kimi/audit-harness.ps1 -Strict
# Exit code 0 = Deploy permitido
# Exit code != 0 = Deploy bloqueado
Checks del Harness:
- ✅ No secrets en código fuente
- ✅ TypeScript compila sin errores
- ✅ ESLint pasa
- ✅ Build exitoso
- ✅ No secrets en bundle de producción
- ✅ Dependency audit (no high severity)
- ✅ .env files en .gitignore
- ✅ HTML sanitization en dangerouslySetInnerHTML
📝 Proceso de Review
Checklist de Seguridad (Para Reviewers)
Antes de aprobar cualquier PR, verificar:
- No hay
SUPABASE_SERVICE_ROLE_KEYen archivos desrc/ - Webhooks validan firma antes de procesar
- Funciones de pago usan CORS restricto (no wildcard)
-
dangerouslySetInnerHTMLusa DOMPurify - Operaciones de wallet usan
atomic_wallet_credit - Pagos/webhooks tienen idempotencia
- Rate limiting en operaciones sensibles
- Env vars correctamente nombradas (
VITE_*vs server-only) - Audit harness pasa (
pwsh .kimi/audit-harness.ps1)
Comandos de Verificación Rápida
# 1. Verificar no hay service role en cliente
grep -r "SERVICE_ROLE" src/ && echo "❌ FALLÓ" || echo "✅ PASS"
# 2. Verificar build no tiene secrets
npm run build
grep -r "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" dist/ && echo "❌ FALLÓ" || echo "✅ PASS"
# 3. Verificar TypeScript
npx tsc --noEmit && echo "✅ PASS" || echo "❌ FALLÓ"
# 4. Ejecutar harness completo
pwsh .kimi/audit-harness.ps1
🚨 Respuesta a Incidentes
Si se expone una SERVICE_ROLE_KEY:
-
INMEDIATO (< 5 min):
- Rotar key en Supabase Dashboard (Settings > API)
- Revocar key anterior
-
URGENTE (< 1 hora):
- Audit logs de Supabase para accesos sospechosos
- Verificar datos no autorizados modificados/leídos
-
Seguimiento (< 24 horas):
- Analizar qué datos fueron expuestos
- Notificar usuarios afectados si aplica (GDPR/privacidad)
- Post-mortem y mejoras al proceso
📚 Referencias
- Supabase RLS Best Practices
- Stripe Webhook Security
- MercadoPago Webhook Notifications
- OWASP XSS Prevention
Última actualización: 2026-01-28
Versión: 1.0
Aprobado por: Senior Security Auditor
Related Documents
Xilinx Constraints
Explains Xilinx XDC constraint ordering, compilation precedence, and key references for FPGA timing closure.
Technical Constraints & Requirements
Defines platform, performance, security, and deployment constraints for a Scottish mountain weather app with native iOS/Android and PWA fallback.
Token Constraints Implementation
Defines a token constraint system that counts tokens, truncates content, and streams responses to stay within an LLM's token limit.
Constraints
Explains how to model constraints as Ceres Solver cost functions in the fuse optimization framework, with a complete code example.