secure-supabase-rls
Skill7 starsChanged 16 days agoMITView on GitHub
Use when adding or changing a Supabase table, view, function or storage bucket exposed through the Data API, or auditing one — enables RLS, writes per-operation role-scoped policies, indexes them, keeps definer functions private, and proves allow and deny with pgTAP.
- Reads credentials
1 other project uses a version of this.
Adopted by
Most recently taken up by
- VelimirMueller/claude-skillsword for word, first seen Oct 11, 2026
Other versions1 project uses a version of this.
Word for word · 1
- VelimirMueller/claude-skills★ 7100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository156 other files it gives its agents.
- skills/swift/swiftui-design-principles/AGENTS.mdAGENTS.md
- skills/swift/ios-code-audit/CLAUDE.mdCLAUDE.md
- skills/swift/swift-focusengine-pro/llms.txtllms.txt
- build-llm-seamSkill
- build-mcp-serverSkill
- build-rag-pipelineSkill
- secure-llm-featuresSkill
- set-up-llm-evalsSkill
- build-nextjs-backendSkill
- build-supabase-edge-functionSkill
- configure-backend-testsSkill
- design-http-apiSkill
- harden-backendSkill
- scaffold-fastapi-serviceSkill
- scaffold-go-serviceSkill
- scaffold-hono-serviceSkill
- set-up-backend-authSkill
- set-up-background-jobsSkill
- set-up-databaseSkill
- set-up-nextjs-supabase-authSkill
- set-up-observabilitySkill
- set-up-supabaseSkill
- build-cliSkill
- release-cliSkill
- set-up-dev-toolchainSkill
- audit-securitySkill
- audit-toolchainSkill
- extend-skillsetSkill
- promote-releaseSkill
- set-up-stack-profileSkill
- write-commit-messagesSkill
- write-pull-requestsSkill
- analyze-complexitySkill
- apply-algorithmsSkill
- apply-design-patternsSkill
- apply-design-principlesSkill
- apply-solidSkill
- choose-data-structureSkill
- debug-systematicallySkill
- enforce-dependency-ruleSkill
- refactor-safelySkill
- slice-and-estimateSkill
- write-clean-testsSkill
- write-readable-codeSkill
- clean-frontend-scaffoldingSkill
- configure-accessibilitySkill
- configure-analyticsSkill
- configure-ciSkill
- configure-error-trackingSkill
- configure-lintingSkill
- configure-test-stackSkill
- configure-typescriptSkill
- create-moduleSkill
- optimize-performanceSkill
- scaffold-frontend-projectSkill
- set-up-authSkill
- set-up-design-systemSkill
- set-up-document-headSkill
- set-up-error-boundariesSkill
- set-up-feature-flagsSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 12
Read .claude/stack-profile.md (database.host: supabase, backend.track).
- Very specific: line 30
All present and supabase test db green → "already in place".
- Distinctive: line 34
Ask of each table, not of the codebase: who may read it, who may write it, and which column ties a row to its owner or tenant.
- Very specific: line 113
Run the curl against the local stack.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Secure Supabase RLS
RLS is the authorization boundary: the publishable key is public, so every row is reachable by anyone who holds a grant, and only a policy narrows it. The app's route guards are UX.
1. Audit current state
Read .claude/stack-profile.md (database.host: supabase, backend.track). Exposed schemas are [api] schemas in supabase/config.toml (default public, graphql_public). Run against the local stack (--linked for a remote):
pnpm supabase db advisors --local --type security # the Security Advisor, same checks as the Dashboard
pnpm supabase db query --local "select n.nspname, c.relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where c.relkind in ('r','p') and n.nspname='public' and not c.relrowsecurity"
pnpm supabase db query --local "select schemaname, tablename, policyname, roles from pg_policies where roles = '{public}'" # policies without a TO clause
pnpm supabase db query --local "select tablename, policyname from pg_policies where qual ~* 'user_metadata|raw_user_meta' or with_check ~* 'user_metadata|raw_user_meta'"
ls supabase/tests/*rls* 2>/dev/nullMore queries (views, definer functions, grants, storage, unwrapped auth.uid()): rls-patterns.md, "Audit queries". Each hit is a finding; report them before changing anything.
2. Decide what to do
- Table without RLS, or with RLS and no policy for an operation the app uses → steps 4–6.
- Policy without
to, withuser_metadata, or with a bareauth.uid()→ fix in place (step 5), new migration. - View without
security_invoker, definer function in an exposed schema → step 5. - No
supabase/tests/<table>_rls.test.sql→ step 6 even if the policies look right. - All present and
supabase test dbgreen → "already in place".
3. Detect what the table is
Ask of each table, not of the codebase: who may read it, who may write it, and which column ties a row to its owner or tenant.
| Shape | Policy basis |
Owner rows (user_id) | (select auth.uid()) = user_id |
Tenant rows (team_id) | membership through a private definer function (rls-patterns.md) |
| Public read, server write | select grant and policy for anon, authenticated; no write grant |
| Server-only (jobs, audit log) | no grant to anon/authenticated at all; RLS on as a backstop |
4. Install only what's missing
Nothing to install: pgTAP ships with the local stack (supabase test new, supabase test db). For tenant tables, supabase-test-helpers is optional; plain set local role works without it (rls-test-patterns.md). Make sure the baseline privileges migration from set-up-supabase exists, so a new table does not start with every grant.
5. Write the migration — grants, RLS, policies, indexes in one file
pnpm supabase migration new secure_notescreate table public.notes (
id uuid primary key default gen_random_uuid(),
user_id uuid not null default auth.uid() references auth.users (id) on delete cascade,
body text not null,
created_at timestamptz not null default now()
);
alter table public.notes enable row level security;
revoke all on table public.notes from anon, authenticated;
grant select, insert, update, delete on table public.notes to authenticated;
create policy "notes: owner reads" on public.notes for select
to authenticated using ((select auth.uid()) = user_id);
create policy "notes: owner inserts" on public.notes for insert
to authenticated with check ((select auth.uid()) = user_id);
create policy "notes: owner updates" on public.notes for update
to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);
create policy "notes: owner deletes" on public.notes for delete
to authenticated using ((select auth.uid()) = user_id);
create index notes_user_id_idx on public.notes (user_id);Rules in force (reasons in rls-patterns.md):
- One policy per operation, each
to authenticated(oranon, authenticatedfor deliberate public reads). (select auth.uid()), never bareauth.uid(): theselectmakes Postgres evaluate it once per statement.- An index on every column a policy filters, with that column first in the index.
- Authorization data comes from
app_metadataor a table, neveruser_metadata(users can edit it). - Views:
create view … with (security_invoker = true) as …. A definer view hands out the rows the table policies withhold. - Definer functions live in a schema that is not exposed (
private), withset search_path = '', schema-qualified names, andrevoke execute … from publicplus an explicit grant. - Storage: policies on
storage.objects, scoped bybucket_idand the owner folder;anonlisting needsstorage.allow_any_operation. See rls-patterns.md, "Storage". - The admin client (
SUPABASE_SECRET_KEY) bypasses all of this: it must filter by the caller's id itself.
6. Test allow and deny with pgTAP
pnpm supabase test new notes_rls.test # creates supabase/tests/notes_rls.test.sqlCover select, insert, update, delete for anon, the owner and a stranger; pair every denied write with a check that the row is intact. A denied using filters silently (zero rows, no error), so assert emptiness, not an error; a missing grant or a failed with check raises 42501. Template and the assertion table: rls-test-patterns.md.
7. Wire into CI
supabase test db and supabase db advisors --local --fail-on error run in the PR job from set-up-supabase. A new table without a test file fails review, not CI: add the audit query for tables lacking RLS to the PR checklist.
8. Verify
pnpm supabase db reset && pnpm supabase test db # Result: PASS
pnpm supabase db advisors --local --type security --level warn # no ERROR; warnings explained or fixed
curl -s "$SUPABASE_URL/rest/v1/notes?select=*" -H "apikey: $SUPABASE_PUBLISHABLE_KEY" # anon: 401/permission denied or [], never rowsRun the curl against the local stack. Expected: no rows for anon, and the test run lists a denial for every operation.
References
- rls-patterns.md — rules, anti-examples of common leaks, audit queries, storage.
- rls-test-patterns.md — pgTAP structure for owner and tenant tables.
- ../set-up-supabase/SKILL.md — baseline privileges, CI.
- ../../core/_shared/security-baseline.md — rule 3, authorization at the data boundary.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

