Fecha: 2026-08-19 Estado: BLOQUEADO (sin acceso service_role para ejecutar migraciones RLS) Producción: SIN CAMBIOS ESTRUCTURALES (ver §Transparencia sobre filas probe)
anon key (ETAPA B, Caso 2), por lo que no se ejecutaron migraciones.docs/database/rls-policies.sql..env.example para migrar credenciales a variables de entorno.database_schema.sql como DEPRECATED con encabezado de advertencia.clientes (DENY anon + policies mínimas)medios_pago (DENY anon + policies mínimas)configuracion SELECTsrc/lib/supabaseClient.js| Tabla | RLS | anon SELECT | anon INSERT | anon UPDATE | anon DELETE |
|---|---|---|---|---|---|
clientes |
NO | ✅ PERMITIDO | ✅ PERMITIDO | ? | ? |
medios_pago |
NO | ✅ PERMITIDO | ✅ PERMITIDO | ? | ? |
configuracion |
Sí | ✅ PERMITIDO (policy USING(true)) |
— | — | — |
usuarios |
Sí | ❌ bloqueado | — | — | — |
sesiones |
Sí | ❌ bloqueado | — | — | — |
ventas |
Sí | ❌ bloqueado | — | — | — |
productos |
Sí | ❌ bloqueado | — | — | — |
gastos |
Sí | ❌ bloqueado | — | — | — |
cierres_turno |
Sí | ❌ bloqueado | — | — | — |
Hallazgo crítico: clientes y medios_pago no sólo son legibles por anon, sino que anon puede INSERTAR filas (verificado empíricamente — ver §Transparencia).
| Tabla | RLS | anon SELECT | anon INSERT | authenticated SELECT | authenticated INSERT | authenticated UPDATE | authenticated DELETE |
|---|---|---|---|---|---|---|---|
clientes |
Sí | ❌ DENY | ❌ DENY | ✅ | ✅ | admin/supervisor | admin |
medios_pago |
Sí | ❌ DENY | ❌ DENY | ✅ | admin | admin | admin |
configuracion |
Sí | ❌ DENY (opcional) | — | ✅ | — | admin | — |
Ver docs/database/rls-policies.sql para el SQL exacto + rollback.
La credencial maurochica23@gmail.com / kennia23 aparece en 22 archivos del proyecto (69 ocurrencias totales):
| Archivo | Línea(s) | Tipo | Acción recomendada |
|---|---|---|---|
database_schema.sql |
414-415 | email + password en INSERT | Rotar password, eliminar del archivo (ya marcado DEPRECATED) |
sql/setup_supabase_project.sql |
515, 518-519 | email + password en INSERT | Eliminar |
sql/supabase_fix_auth.sql |
101, 114-115, 175-176, 193-194, 216, 227-228 | email + password múltiples | Eliminar |
README.md |
102-103, 142-143, 329, 337 | email + password en docs | Eliminar password |
DEPLOYMENT.md |
113 | password en docs | Eliminar |
INSTRUCCIONES_SUPABASE.md |
101-102 | email + password | Eliminar |
GITHUB_SETUP.md |
190-191 | email + password | Eliminar |
MIGRACION_SUPABASE_COMPLETA.md |
94-95 | email + password | Eliminar |
REPORTE_CONEXION_SUPABASE.md |
80-81 | email + password | Eliminar |
CONFIGURACION_EMAILS.md |
110-111 | email + password | Eliminar |
MOBILE_TROUBLESHOOTING.md |
55, 90, 160, 164-165, 185, 192, 220 | email + password múltiples | Eliminar |
configurar_supabase.html |
106-107 | email + password en JS | Eliminar |
fix_production_auth.html |
148-149 | email + password en JS | Eliminar |
debug_auth_production.html |
150-151 | email + password en JS | Eliminar |
debug-auth-sync.html |
143-144 | email + password en JS | Eliminar |
debug_login_connection.html |
34-35 | email + password en input | Eliminar |
verificar_acceso_admin.html |
118-119 | email + password en HTML | Eliminar |
verificar_credenciales.js |
6-7 | email + password en const | Eliminar |
verificar_acceso_admin.js |
45 | email en función | Eliminar |
limpiar_sistema.js |
44, 56, 107 | email en logs/config | Eliminar |
js/login.js |
110-111, 292, 441-442 | email + password pre-llenados | Eliminar |
index_vanilla.html |
1379 | email en comparación | Eliminar |
_config.yml |
13 | email en config Jekyll | Evaluar |
ejecutar_limpieza.html |
95 | email en HTML | Eliminar |
maurochica23@gmail.com.usuarios.password_hash vía la edge function user-set-password o SQL: UPDATE usuarios SET password_hash = hash_password('NUEVA') WHERE email = 'maurochica23@gmail.com';src/), pero database_schema.sql y sql/*.sql son referenciados.debug_*.html, verificar_*.js, js/login.js, index_vanilla.html son legacy y candidatos a eliminación.database_schema.sql en la raíz era tratado como fuente de verdad.clientes, medios_pago, ventas, venta_items, cierres_turno, cliente_id en sesiones, monto_*, es_critico_arqueo, etc.docs/database/production-schema.sql (referencia, no script ejecutable).pg_dump --schema-only. El schema de referencia se construyó inferencialmente. Para una versión autoritativa, ejecutar con service-role:
pg_dump --schema-only --no-owner --no-privileges "postgresql://postgres:[PASSWORD]@db.stjbtxrrdofuxhigxfcy.supabase.co:5432/postgres" > docs/database/production-schema-authoritative.sql
database_schema.sql marcado como DEPRECATED con encabezado de advertencia.docs/database/production-schema.sql como referencia actualizada.docs/database/rls-policies.sql con el plan de migración + rollback.| # | Problema | Corregido | Pendiente |
|---|---|---|---|
| F1 | POS no transaccional: stock descontado sin venta | — | ✅ Sprint 0.2 |
| F2 | finalizarSesion no transaccional: sesión finalizada sin venta |
— | ✅ Sprint 0.2 |
| F3 | Race condition stock (read-modify-write) | — | ✅ Sprint 0.2 |
| F4 | anularSesion no devuelve stock |
— | ✅ Sprint 0.2 |
| F5 | Fuentes múltiples sesiones vs ventas |
— | ✅ Sprint 0.2 |
| F6 | Auth dual | — | ✅ Sprint 0.2 |
| F7 | venta_items insert uno-a-uno (items parciales) |
— | ✅ Sprint 0.2 |
| F8 | cierres_turno + items no transaccionales |
— | ✅ Sprint 0.2 |
docs/database/canonical-data-model.md — define fuente de verdad por entidad, relaciones, brechas y reglas de integridad transaccional para Sprint 2.Sprint 0.1 es sólo seguridad + documentación. La integridad transaccional se diseña aquí pero se implementa en Sprint 0.2 / Sprint 2.
| Archivo | Acción | Tipo |
|---|---|---|
docs/database/production-schema.sql |
Creado | Documentación |
docs/database/rls-policies.sql |
Creado | Plan SQL (no ejecutado) |
docs/database/canonical-data-model.md |
Creado | Documentación |
docs/security/sprint-0.1-report.md |
Creado | Este reporte |
.env.example |
Creado | Configuración (sólo nombres, sin valores) |
database_schema.sql |
Modificado | Encabezado DEPRECATED añadido (sin tocar el cuerpo legacy) |
No se modificó: src/, hooks, componentes, SQL productivo, Supabase, RLS, ni datos.
NINGUNA.
anon key disponible → Caso 2 → no ejecutar.docs/database/rls-policies.sql con rollback, pero requieren service_role para ejecutarse.NO APLICA — no se ejecutaron migraciones.
Plan de verificación para cuando se ejecuten (con service-role):
| Check | Esperado | Método |
|---|---|---|
| anon SELECT clientes | DENIED | supabase.from('clientes').select('*').limit(1) sin sesión → error 42501 o 0 filas |
| anon SELECT medios_pago | DENIED | Igual |
| authenticated SELECT clientes | PASS | Login + select → filas |
| authenticated INSERT cliente | PASS | Login + insert → ok |
| login | PASS | Probar login con credencial rotada |
| CRM clientes | PASS | Abrir /clientes → lista carga |
| ventas | PASS | Abrir /ventas → lista carga |
| salas | PASS | Abrir /salas → salas cargan |
| sesiones | PASS | Abrir sesión → funciona |
| stock | PASS | Abrir /stock → productos cargan |
| reportes | PASS | Abrir /reportes → datos cargan |
| cierre | PASS | Abrir /cierre-turno → funciona |
| finalizar sesión | PASS | Finalizar sesión → venta se registra |
| agregar producto | PASS | Agregar producto a sesión → stock baja |
Cada migración en docs/database/rls-policies.sql tiene su rollback documentado inline:
DROP POLICY + ALTER TABLE ... DISABLE ROW LEVEL SECURITY.TO authenticated, anon.Principio: nunca usar DROP POLICY * sin conocer las originales. Las policies originales de clientes y medios_pago son inexistentes (no hay RLS), por lo que el rollback es simplemente deshabilitar RLS.
finalizar_venta_pos(p_venta jsonb) que ejecute atómicamente: INSERT venta + INSERT venta_items + UPDATE productos + INSERT movimientos_stock.pg en una transacción.UPDATE productos SET stock = stock - $1 WHERE id = $2 AND stock >= $1 RETURNING stock.finalizar_sesion(p_sesion_id uuid, p_metodo_pago text, p_montos jsonb) que ejecute atómicamente: UPDATE sesiones + INSERT ventas + (sincronizar totales).anularSesion para que devuelva stock (INSERT movimientos_stock tipo=’devolucion’ + UPDATE productos).ventas.total es fuente oficial. sesiones.total_general se elimina o se mantiene como cache con trigger.sesiones.finalizada (usar sólo estado).pagos o se mantienen columnas monto_*.medios_pago vía FK.cierres_turno + cierre_turno_items.useSalas / realtimeuseSalas() (Salas, Ajustes, MonitorSalasActivas, y 10 modales).salas-hook-rt-<random> sobre sesiones.dashboard-rt-v2), TV (tv-sesiones), ni EventLive (event-live-sesiones).usuarios.password_hash y quedar sólo con Supabase Auth, o eliminar Supabase Auth.password_hash: actualizar edge function user-set-password, useAuth.js, y todas las migraciones SQL que la referencian.src/lib/supabaseClient.jsimport.meta.env.VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEY..env.local (no commiteado) con los valores reales.Durante el precheck de RLS (ETAPA C), se probaron INSERTs anon sobre clientes y medios_pago para verificar si RLS bloqueaba escrituras. No las bloqueó, y se crearon 2 filas basura en producción:
| Tabla | ID | Identificador | Fecha |
|---|---|---|---|
clientes |
33 | nombre='__GC_PROBE__' |
2026-08-19 11:31:52 -05 |
medios_pago |
3 | banco='__GC_PROBE__' |
2026-08-19 11:31:53 -05 |
No se eliminaron porque el sprint prohíbe DELETE sin autorización. La limpieza está documentada como Migración 3 en docs/database/rls-policies.sql y requiere autorización explícita.
Este incidente refuerza la severidad del hallazgo: anon no sólo lee PII, sino que puede escribir datos arbitrarios en clientes y medios_pago.
SPRINT 0.1 — BLOQUEADO
Razón: no se dispone de service_role para ejecutar las migraciones RLS. El plan está completo, documentado y con rollback, pero requiere que el propietario ejecute el SQL en Supabase SQL Editor (o proporcione service-role al agente).
clientes y medios_pago con rollback.env.example creadodatabase_schema.sql marcado DEPRECATEDanularSesion documentadouseSalas/realtime documentadosupabaseClient.js a env vars (requiere crear .env.local con valores reales)pg_dump (requiere service-role)| Criterio | Estado |
|---|---|
| clientes no legible por anon | ❌ PENDIENTE (plan listo, no ejecutado) |
| medios_pago no legible por anon | ❌ PENDIENTE (plan listo, no ejecutado) |
| credencial rotada | ❌ PENDIENTE (instrucciones listas) |
| password eliminado del repo | ❌ PENDIENTE (inventario listo) |
| secretos frontend en env vars | ❌ PENDIENTE (.env.example listo) |
| schema real documentado | ✅ (inferencial, limitado) |
| policies reales documentadas | ✅ (plan con rollback) |
| modelo canónico documentado | ✅ |
| no hay cambios funcionales accidentales | ✅ |
| build PASS | ✅ (no se tocó src/) |
| login PASS | NOT TESTED (no se modificó auth) |
| CRM PASS | NOT TESTED (no se modificó RLS aún) |
| rollback documentado | ✅ |
docs/database/rls-policies.sql):
supabaseClient.js a env vars, y crear .env.local.No iniciar Sprint 0.2 hasta que los riesgos CRÍTICOS (RLS clientes/medios_pago + credencial) estén cerrados en producción.