RLS Audit — authorized self-audit
Precondition: the user owns the project being audited. This is defensive security against your own infrastructure — confirm the project ref belongs to the user before starting. Do not run this against a third party's project.
Parity partner: /rls-enforce. This skill finds and reports; that one closes the gaps.
Sweep
- Enumerate the anon surface: every table, view, RPC, storage bucket, and edge function reachable with the public anon key (REST catalog + storage list + functions list).
- Test as anon: read/insert/update/delete attempts against each surface — read-only where possible; any write test uses throwaway rows that are rolled back/deleted immediately.
- Test as a self-registered user: sign up a scratch account, repeat the sweep — cross-tenant reads are the killer (can user A see workspace B's rows?).
- Edge functions: which are world-callable? Do they verify JWT + ownership INSIDE the function? This matters most when functions are deployed with
--no-verify-jwt, which moves auth from the platform into your code. Check for module-level caches that can bleed across tenants on warm instances — a per-tenant value cached at module scope gets served to whoever hits that instance next. - Service-role hygiene: service-role actions must be logged with full detail; flag unlogged service-role paths and missing audit tables.
Output — the findings (the handoff to /rls-enforce)
This skill FINDS; it does not fix. Produce a dated findings doc in the repo's docs security folder (.docs/security/ by default, or whatever the repo's runbook/convention names as its docs folder) and a structured findings list that /rls-enforce consumes directly. Each finding:
surface— table / view / RPC / bucket / edge function.table_class— owner-scoped | reference/config | server-internal | public-catalog (best inference; /rls-enforce confirms).gap— the specific hole: RLS-off · permissiveUSING(true)write · PII/cost column client-readable · world-callable definer fn · cross-tenant read · missing WITH CHECK · unlogged service-role path.severity— Critical/High/Medium/Low, ranked worst-first.reproduction— exact request + what came back. Receipts, not theory. Then hand off: "run /rls-enforce to close these" — remediation, migrations, and re-verify live there. Do NOT write fix migrations in this skill.
Rules
- Read-only exposure audit: production data untouched except the throwaway scratch rows described above; no deletes of real rows, no load/stress tests.
- Parity: the findings shape above is the exact input /rls-enforce expects — audit finds, enforce closes, both re-test with the same sweep.