🔗 Visit the website for full documentation and examples.
Catch Supabase RLS security vulnerabilities before they reach production.
- Security vulnerability detection
- RLS coverage reporting (see what each role can access)
- Policy change detection (snapshot & diff)
- Smart schema discovery
- RLS policy testing (tables + storage buckets)
- Real user context testing
- CI/CD ready
- Zero configuration
npm install -g supashieldSet your Supabase database URL:
export SUPASHIELD_DATABASE_URL="postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres"Get this from: Supabase Dashboard → Settings → Database → Connection string → Transaction pooler
Important: Use the Transaction pooler connection string (port 6543), not the Direct connection. Transaction pooler is IPv4 compatible and works everywhere.
Note: DATABASE_URL is also supported for backwards compatibility.
supashield audit # scan for common RLS security issues
supashield coverage # generate RLS coverage report
supashield init # discover tables and storage buckets
supashield test # test all table RLS policies
supashield test-storage # test storage bucket RLS policies
supashield test --table public.users # test specific table
supashield test --as-user admin@company.com # test with real user
supashield snapshot # save current RLS policy state
supashield diff # compare current state vs snapshot
supashield users # list users from auth.users for testing
supashield export-pgtap -o tests.sql # export tests to pgTap formatTesting public.users: anonymous_user: SELECT: ALLOW (expected DENY) - MISMATCH INSERT: DENY (expected DENY) - PASS authenticated_user: SELECT: ALLOW (expected ALLOW) - PASS INSERT: DENY (expected ALLOW) - MISMATCH
Results: 2 passed, 2 failed 2 policy mismatches detected
## Configuration (`.supashield/policy.yaml`)
```yaml
tables:
public.users:
test_scenarios:
- name: anonymous_user
jwt_claims: {}
expected: { SELECT: DENY, INSERT: DENY, UPDATE: DENY, DELETE: DENY }
- name: authenticated_user
jwt_claims: { sub: "user-123", role: "authenticated" }
expected: { SELECT: ALLOW, INSERT: ALLOW, UPDATE: ALLOW, DELETE: ALLOW }
storage_buckets:
avatars:
test_scenarios:
- name: anonymous_user
jwt_claims: {}
expected: { SELECT: DENY, INSERT: DENY, UPDATE: DENY, DELETE: DENY }
- name: authenticated_user
jwt_claims: { sub: "user-123", role: "authenticated" }
expected: { SELECT: ALLOW, INSERT: ALLOW, UPDATE: ALLOW, DELETE: ALLOW }
RLS Testing is Hard
- Manual testing doesn't scale
- Complex permission logic is error-prone
- Security bugs are expensive to fix in production
SupaShield Makes it Easy
- Automatically discovers your schema
- Tests all CRUD operations for each role
- Validates real user permissions
- Integrates with your CI/CD pipeline
- run: supashield test
env:
SUPASHIELD_DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}- All operations use transactions and rollbacks
- No data is ever persisted during testing
Got an idea? Open an issue or ping me on X/Twitter.
This tool tests RLS policies using safe, rolled-back transactions. Always test on staging/local environments first. Use at your own risk. Not liable for data loss.
MIT