CommunityProgramación y desarrollogithub.com

atokim4/supabase-rls-audit

Audita el nivel de seguridad de las filas (RLS) de Supabase directamente desde las migraciones del repositorio. Encuentra tablas públicas sin RLS, políticas permisivas, columnas de privilegios actualizables por usuarios, vistas que evaden RLS, funciones SECURITY DEFINER inseguras y buckets de almacenamiento público.

¿Qué es supabase-rls-audit?

Este skill de agente de IA (compatible con Claude Code, Cursor, Codex y otros) analiza los archivos `.sql` de migraciones de Supabase de un proyecto para detectar 9 patrones de configuración insegura de Row Level Security (RLS), todo sin conexión a la base de datos. Es ideal para revisar la seguridad antes del lanzamiento, después de escribir una migración, o al revisar proyectos generados por Lovable, Bolt o Cursor. El analizador reproduce tablas, políticas, vistas, funciones y buckets de almacenamiento en orden y reporta fallos con severidad de error o advertencia, indicando el archivo y la línea exacta. También ofrece sugerencias concretas de corrección, como agregar políticas de RLS acotadas a roles, revocar privilegios sobre columnas sensibles, fijar `search_path` en funciones SECURITY DEFINER, y usar `security_invoker = true` en vistas. El resultado se entrega en JSON (con resumen de errores y advertencias) o texto explicativo. No reemplaza pruebas reales con usuario anónimo y autenticado, pero es una primera línea de defensa muy efectiva.

Compatible con✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/atokim4/vibe-preflight/tree/HEAD/skills/supabase-rls-audit

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Auditoría RLS de Supabase

Este skill ejecuta la parte de base de datos del escáner vibe-preflight (scripts/preflight.mjs junto a este archivo, Node 20+, sin dependencias, sin red). Lee los archivos .sql del proyecto (migraciones de Supabase, schema.sql), reproduce tablas, políticas, vistas, funciones, permisos y buckets de almacenamiento en orden, e informa 9 problemas. No se conecta a una base de datos.

Ejecutarlo

Desde la raíz del proyecto:

node ${CLAUDE_SKILL_DIR}/scripts/preflight.mjs . --only database --format json

Claude Code reemplaza ${CLAUDE_SKILL_DIR}. Otros agentes: usar la carpeta de este skill, por ejemplo .agents/skills/supabase-rls-audit o .claude/skills/supabase-rls-audit.

El código de salida 1 significa al menos un error. Añadir --fail-on warning para que falle también con advertencias, --explain (salida de texto) para saber por qué y cómo solucionar cada uno. JSON: { summary: { error, warning }, findings: [{ rule, severity, message, file, line }] }.

Si el resultado dice 0 archivos SQL, las políticas probablemente viven solo en el panel de control. Pide al usuario ejecutar supabase db pull (escribe el esquema remoto en una migración) y escanear de nuevo. No informes el proyecto como limpio si el escaneo está vacío.

Las comprobaciones

ReglaNivelSignificado
rls-disablederrorUna tabla public nunca recibe enable row level security, por lo que cualquiera con la clave anónima puede leerla y escribirla a través de la API.
rls-permissive-policyerror en escritura, advertencia en lecturausing (true) o with check (true). En inserción/actualización/eliminación cualquier usuario puede cambiar las filas de otros usuarios. En selección cada fila es pública.
rls-no-policyadvertenciaRLS activada, sin políticas: la API lo deniega todo. Aceptable para tablas solo de servidor; una lista vacía silenciosa si el navegador lo lee.
rls-update-privileged-columnadvertenciaLos usuarios pueden actualizar su propia fila y esta tiene plan, role, is_admin, credits o similar. RLS limita filas, no columnas.
view-bypasses-rlserrorUna vista sin security_invoker = true se ejecuta como su propietario y evita RLS en las tablas subyacentes.
auth-users-exposederrorUna vista en public selecciona de auth.users.
definer-search-pathadvertenciaUna función security definer sin set search_path.
definer-function-anonerror / advertenciaUna función security definer en public a la que anon puede llamar en /rest/v1/rpc/<nombre>. Error con un permiso explícito a anon, advertencia para el permiso predeterminado de Supabase.
storage-public-bucketerror / advertenciaUn bucket público. Error cuando una política de selección en storage.objects también permite que cualquiera lo liste.

Cómo solucionarlo

Siempre escribe una migración nueva. Nunca edites una que ya se haya ejecutado en una base de datos.

Políticas faltantes o permisivas: preguntar qué acceso necesita la aplicación, luego acotar cada política a un rol y al propietario.

alter table public.notes enable row level security;
create policy "notes: leer propias" on public.notes
  for select to authenticated using ((select auth.uid()) = user_id);
create policy "notes: insertar propias" on public.notes
  for insert to authenticated with check ((select auth.uid()) = user_id);

Columnas privilegiadas: permitir actualizaciones solo en las columnas que los usuarios pueden cambiar.

revoke update on public.profiles from authenticated;
grant update (display_name, avatar_url) on public.profiles to authenticated;

Vistas: alter view public.<nombre> set (security_invoker = true); (Postgres 15+, todos los proyectos actuales de Supabase). Una vista materializada no puede usar RLS: muévela fuera de public o revoca select a anon, authenticated. Para auth.users, copia los campos compartibles en public.profiles con un disparador de registro.

Funciones SECURITY DEFINER: añadir set search_path = '' y escribir cada nombre con su esquema. Luego restringir quién puede llamarla:

revoke execute on function public.admin_set_plan(uuid, text) from public, anon, authenticated;
grant execute on function public.admin_set_plan(uuid, text) to service_role;

Revocar solo de public no es suficiente en Supabase, porque anon tiene su propio permiso en funciones nuevas.

Almacenamiento: haz privados los buckets privados, limita las lecturas a la carpeta del propietario ((storage.foldername(name))[1] = (select auth.uid())::text) y sirve URL firmadas. Las imágenes de productos y logotipos pueden permanecer públicas.

Un hallazgo que es intencional (un catálogo público, una tabla solo de servidor) se puede silenciar con -- vibe-preflight-ignore <id-de-regla> en la línea o la línea anterior. Escribe el motivo en el mismo comentario.

Informe

  1. Abre cada línea reportada y confírmala contra el SQL antes de cambiar nada. El escáner lee texto SQL y puede equivocarse; dilo cuando ocurra.
  2. Agrupa el resumen por regla. Primero los errores, luego las advertencias que confirmaste con el usuario.
  3. Vuelve a ejecutar el escaneo después de la corrección e informa el nuevo resultado.

Limitaciones

  • Solo lo que está en archivos .sql. Cambios solo en el panel de control, otros esquemas y lo que hace una función dentro de su cuerpo no se comprueban.
  • Un resultado limpio significa que estos 9 patrones no se encontraron. No prueba que las políticas coincidan con las reglas de la aplicación. Probar como un usuario anónimo real y un usuario autenticado (el cliente de Supabase con la clave anónima) sigue valiendo la pena.
  • Para el lado del navegador y entorno (clave service_role en código cliente, secretos en variables NEXT_PUBLIC_, .env commiteado) ejecuta el escaneo completo sin --only, o usa el skill vibe-preflight.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Skills relacionados