atokin4/supabase-rls-audit

Audita a segurança em nível de linha (RLS) do Supabase a partir das migrações do repositório. Detecta tabelas públicas sem RLS, políticas permissivas, updades em colunas privilegiadas, views que ignoram RLS, funções SECURITY DEFINER inseguras e buckets públicos de armazenamento.

O que é supabase-rls-audit?

Esta skill executa a parte de banco de dados do scanner 'vibe-preflight' (`scripts/preflight.mjs` – Node 20+, sem dependências, sem rede) para auditar a segurança em nível de linha (RLS) do Supabase a partir dos arquivos `.sql` de migração presentes no repositório local. Ela analisa arquivos SQL (migrations do Supabase, `schema.sql`), reproduz a ordem de criação de tabelas, políticas, views, funções, grants e buckets de armazenamento, e reporta 9 problemas comuns de configuração de RLS, como tabelas públicas sem RLS, políticas com `using (true)` ou `with check (true)`, updades em colunas privilegiadas (plano, papel, créditos), views que ignoram RLS, views sobre `auth.users`, funções SECURITY DEFINER sem `search_path` fixo ou chamáveis pelo anônimo, e buckets públicos de armazenamento. Suporta Claude Code (via variável `${CLAUDE_SKILL_DIR}`) e outros agentes como Cursor, Codex e Bolt, podendo ser colocada em pastas como `.agents/skills/supabase-rls-audit` ou `.claude/skills/supabase-rls-audit`. A saída pode ser em JSON (com resumo de erros e avisos) ou texto com explicações (modo `--explain`). É útil antes de lançar um app Supabase, após escrever uma migração, ou ao revisar builds gerados por ferramentas como Lovable, Bolt ou Cursor que usam Supabase.

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

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Auditoria RLS do Supabase

Esta skill executa a parte de banco de dados do scanner vibe-preflight (scripts/preflight.mjs ao lado deste arquivo, Node 20+, sem dependências, sem rede). Ela lê os arquivos .sql do projeto (migrations do Supabase, schema.sql), reproduz a ordem de tabelas, políticas, views, funções, grants e buckets de armazenamento, e reporta 9 problemas. Não se conecta a um banco de dados.

Como executar

A partir da raiz do projeto:

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

Claude Code preenche ${CLAUDE_SKILL_DIR}. Outros agentes: use a pasta desta skill, ex.: .agents/skills/supabase-rls-audit ou .claude/skills/supabase-rls-audit.

Código de saída 1 significa pelo menos um erro. Adicione --fail-on warning para falhar também em avisos, --explain (saída de texto) para explicações de como corrigir cada um. JSON: { summary: { error, warning }, findings: [{ rule, severity, message, file, line }] }.

Se o resultado disser 0 SQL files, as políticas provavelmente existem apenas no dashboard. Peça ao usuário para executar supabase db pull (ele escreve o schema remoto em uma migration) e escanear novamente. Não reporte o projeto como limpo a partir de uma varredura vazia.

As verificações

RegraNívelO que significa
rls-disablederroUma tabela public nunca recebe enable row level security, então qualquer pessoa com a chave anônima pode ler e escrever nela através da API.
rls-permissive-policyerro em escrita, aviso em leiturausing (true) ou with check (true). Em insert/update/delete qualquer usuário pode alterar linhas de outros usuários. Em select toda linha é pública.
rls-no-policyavisoRLS ativado, sem políticas: a API nega tudo. Tudo bem para tabelas apenas de servidor; resulta em lista vazia silenciosa se o navegador tentar ler.
rls-update-privileged-columnavisoUsuários podem atualizar sua própria linha e ela contém plan, role, is_admin, credits ou similar. O RLS limita linhas, não colunas.
view-bypasses-rlserroUma view sem security_invoker = true executa como seu dono e ignora o RLS nas tabelas que a compõem.
auth-users-exposederroUma view em public seleciona de auth.users.
definer-search-pathavisoUma função security definer sem set search_path.
definer-function-anonerro / avisoUma função security definer em public que o anônimo pode chamar em /rest/v1/rpc/<nome>. Erro com um grant explícito para anônimo, aviso para o grant padrão do Supabase.
storage-public-bucketerro / avisoUm bucket público. Erro quando uma política de select em storage.objects também permite que qualquer pessoa liste o bucket.

Como corrigir

Sempre escreva uma nova migração. Nunca edite uma que já foi executada em um banco de dados.

Políticas ausentes ou permissivas: pergunte qual acesso o app precisa, então escopo cada política para um papel e para o dono.

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

Colunas privilegiadas: permita atualizações apenas nas colunas que os usuários podem alterar.

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

Views: alter view public.<nome> set (security_invoker = true); (Postgres 15+, todos os projetos Supabase atuais). Uma view materializada não pode usar RLS: mova-a para fora de public ou revogue select de anon, authenticated. Para auth.users, copie os campos compartilháveis para public.profiles com um gatilho de cadastro.

Funções SECURITY DEFINER: adicione set search_path = '' e escreva cada nome com seu schema. Em seguida, restrinja quem pode chamá-la:

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;

Revogar apenas de public não é suficiente no Supabase, porque o anônimo tem seu próprio grant em funções novas.

Armazenamento: torne buckets privados privados, escopo leituras para a pasta do dono ((storage.foldername(name))[1] = (select auth.uid())::text) e sirva URLs assinadas. Imagens de produtos e logotipos podem permanecer públicos.

Um achado que é intencional (um catálogo público, uma tabela apenas de servidor) pode ser silenciado com -- vibe-preflight-ignore <id-da-regra> na linha ou na linha acima dela. Explique o motivo no mesmo comentário.

Relatório

  1. Abra cada linha reportada e confirme-a contra o SQL antes de alterar qualquer coisa. O scanner lê o texto SQL e pode estar errado; diga quando isso acontecer.
  2. Agrupe o resumo por regra. Erros primeiro, depois os avisos que você confirmou com o usuário.
  3. Execute a varredura novamente após a correção e reporte o novo resultado.

Limitações

  • Apenas o que está em arquivos .sql. Alterações feitas apenas no dashboard, outros schemas e o que uma função faz dentro de seu corpo não são verificados.
  • Um resultado limpo significa que esses 9 padrões não foram encontrados. Não prova que as políticas correspondem às regras do aplicativo. Testar como um verdadeiro anônimo e usuário logado (o cliente Supabase com a chave anônima) ainda vale a pena.
  • Para o lado do navegador e ambiente (chave service_role em código cliente, segredos em variáveis NEXT_PUBLIC_, .env commitado) execute a varredura completa sem --only, ou use a skill vibe-preflight.

Individual skills in this repo

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

Habilidades Relacionadas