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
DunApp PWA - Project Constraints
> **⚠️ KRITIKUS DOKUMENTUM**
Technical Constraints & Requirements
This document defines the technical constraints, requirements, and architecture decisions for the Scottish Mountain Weather App development.
Specifying version constraints
lastupdated: "2025-11-18"
Constraints and Scoring Rules
This document describes **all constraints** and scoring rules for the OptaPlanner solver.