Community编程与开发github.com

atokim4/supabase-rls-audit

审计仓库中 Supabase 迁移文件的行级安全策略。自动检测公共表缺少 RLS、宽松策略、特权列更新等 9 类安全问题。

supabase-rls-audit 是什么?

这是一个针对 Supabase 项目的行级安全审计技能,无需连接真实数据库即可运行。它读取项目中的 SQL 文件(如 Supabase 迁移或 schema.sql),顺序回放表、策略、视图、函数、授权和存储桶定义,并检测 9 种常见安全问题:公共表没有 RLS、有 RLS 但无策略、using(true)/with check(true) 宽松策略、允许用户修改计划/角色/积分等敏感列的更新策略、绕过 RLS 的视图、暴露 auth.users 的视图、SECURITY DEFINER 函数缺少固定 search_path 或被 anon 角色调用、以及公开存储桶。该技能适用于 Claude Code、Cursor 等 AI 代理,在用户要求检查 Supabase RLS、启动应用前、编写迁移后或审查 Lovable/Bolt/Cursor 构建时自动触发。

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

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Supabase RLS 审计

本技能运行 vibe-preflight 扫描器的数据库部分(scripts/preflight.mjs,位于本文件旁边,Node 20+,无依赖,无网络连接)。它读取项目中的 .sql 文件(Supabase 迁移、schema.sql),按顺序回放表、策略、视图、函数、授权和存储桶定义,并报告 9 类问题。它不连接数据库。

运行

从项目根目录运行:

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

Claude Code 会自动填充 ${CLAUDE_SKILL_DIR}。对于其他代理:使用本技能的文件夹,例如 .agents/skills/supabase-rls-audit 或 .claude/skills/supabase-rls-audit。

退出码 1 表示至少有一个错误。添加 --fail-on warning 可同时将警告视为失败,--explain(文本输出)可解释每个问题的原因和修复方法。JSON 输出:{ summary: { error, warning }, findings: [{ rule, severity, message, file, line }] }。

如果结果显示 0 SQL files,则策略可能仅存在于仪表盘中。请用户运行 supabase db pull(它会将远程模式写入迁移)并重新扫描。不要因空扫描就报告项目是干净的。

检查项

规则级别含义
rls-disablederrorpublic 表从未启用 enable row level security,因此任何拥有 anon 密钥的人都可以通过 API 读写它。
rls-permissive-policy写入 error,读取 warningusing (true) 或 with check (true)。在插入/更新/删除时,任何用户都可以修改其他用户的行。在查询时,每一行都是公开的。
rls-no-policywarningRLS 已启用但无策略:API 拒绝一切。对于仅限服务器使用的表来说没问题;但如果浏览器读取,则会收到空列表。
rls-update-privileged-columnwarning用户可以更新自己的行,但该行包含 plan、role、is_admin、credits 等列。RLS 限制行,但不限制列。
view-bypasses-rlserror没有 security_invoker = true 的视图以其所有者身份运行,并跳过底层表上的 RLS。
auth-users-exposederrorpublic 模式中的视图查询了 auth.users。
definer-search-pathwarningsecurity definer 函数没有 set search_path。
definer-function-anonerror / warningpublic 模式中的 security definer 函数,且 anon 角色可以通过 /rest/v1/rpc/<name> 调用。显式授权给 anon 时为 error,Supabase 默认授权时为 warning。
storage-public-bucketerror / warning公开的存储桶。当 storage.objects 上的查询策略也允许任何人列出内容时为 error。

如何修复

始终编写新的迁移文件。切勿编辑已在数据库上运行过的迁移。

缺少策略或策略过于宽松:询问应用需要什么访问权限,然后将每个策略限定到特定角色和数据所有者。

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);

特权列:只允许更新用户可更改的列。

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

视图:alter view public.<name> set (security_invoker = true);(需要 Postgres 15+,所有当前 Supabase 项目均已支持)。物化视图无法使用 RLS:将其移出 public 或撤销 anon, authenticated 角色的查询权限。对于 auth.users,使用注册触发器将可共享字段复制到 public.profiles 中。

SECURITY DEFINER 函数:添加 set search_path = '' 并使用模式全名编写每个对象。然后限制谁可以调用它:

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;

在 Supabase 上,仅从 public 撤销是不够的,因为 anon 对新函数有自己的授权。

存储:将私有桶设为私有,将读取范围限定为所有者文件夹((storage.foldername(name))[1] = (select auth.uid())::text),并提供签名 URL。产品图片和 Logo 可以保持公开。

如果某个发现是预期行为(例如公共目录、仅服务器使用的表),可以在该行或其上一行添加 -- vibe-preflight-ignore <rule-id> 并附上原因注释来忽略。

报告

  1. 在修改任何内容之前,打开每个报告的行并与 SQL 对照确认。扫描器仅读取 SQL 文本,可能出错;如果出错请明确指出。
  2. 按规则对摘要进行分组。先列出错误,然后是经用户确认的警告。
  3. 修复后重新运行扫描并报告新结果。

限制

  • 仅检查 .sql 文件中的内容。仪表盘中的更改、其他模式以及函数体内部的操作不会被检查。
  • 结果为干净仅表示未发现这 9 种模式。它并不能证明策略与应用规则匹配。使用真实的 anon 用户和已登录用户(通过 Supabase 客户端和 anon 密钥)进行测试仍然是必要的。
  • 对于浏览器和环境端(客户端代码中的 service_role 密钥、NEXT_PUBLIC_ 变量中的秘密、已提交的 .env),请运行不带 --only 的完整扫描,或使用 vibe-preflight 技能。

Individual skills in this repo

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

相关技能