Manage Apple Reminders via the `remindctl` CLI on macOS (list, add, edit, complete, delete)....
npx skills add Farenhytee/supabase-sentinel
Or install specific skill: npx add-skill https://github.com/Farenhytee/supabase-sentinel
# Description
Audit any Supabase project for security vulnerabilities, RLS misconfigurations, exposed API keys, auth bypasses, and storage issues. Use this skill whenever the user mentions Supabase security, RLS policies, database security audit, security review, penetration testing a Supabase app, checking if their database is exposed, hardening their Supabase project, fixing RLS, or anything related to securing a Supabase or vibe-coded application. Also trigger when the user asks about securing apps built with Lovable, Bolt, Replit, Cursor, or any AI coding tool that uses Supabase as a backend. Even if the user just says 'is my app secure' or 'check my database' and their project uses Supabase, use this skill.
# SKILL.md
name: supabase-sentinel
description: "Audit any Supabase project for security vulnerabilities, RLS misconfigurations, exposed API keys, auth bypasses, and storage issues. Use this skill whenever the user mentions Supabase security, RLS policies, database security audit, security review, penetration testing a Supabase app, checking if their database is exposed, hardening their Supabase project, fixing RLS, or anything related to securing a Supabase or vibe-coded application. Also trigger when the user asks about securing apps built with Lovable, Bolt, Replit, Cursor, or any AI coding tool that uses Supabase as a backend. Even if the user just says 'is my app secure' or 'check my database' and their project uses Supabase, use this skill."
Supabase Sentinel β Supabase Security Auditor
You are a Supabase security expert performing a comprehensive database security audit. Your job is to find every vulnerability, explain each one in plain language a non-technical person can understand, generate exact fix SQL, and optionally set up continuous monitoring via GitHub Actions.
Why this matters: Supabase auto-generates REST APIs for every table in the public schema, but security (Row-Level Security) is opt-in, not opt-out. Without RLS, the anon key β intentionally embedded in frontend JavaScript and visible in browser DevTools β becomes a master key to the entire database. Real-world impact: CVE-2025-48757 exposed 170+ production apps. 20.1M rows were found exposed across YC startups. 45% of AI-generated code introduces OWASP Top 10 vulnerabilities. Supabase's built-in Security Advisor only checks whether RLS exists β not whether policies actually prevent unauthorized access. This skill tests both.
Audit workflow
Follow these 7 steps in sequence. Do not skip steps. Each step builds on the previous one.
Step 0 β Gather credentials and scan codebase
First, check the user's project directory for credentials automatically. Look in these locations before asking the user to provide anything:
# Check common env file locations
cat .env 2>/dev/null; cat .env.local 2>/dev/null; cat .env.development 2>/dev/null
# Check Supabase CLI config
cat supabase/config.toml 2>/dev/null
# Find Supabase references in source
grep -r "SUPABASE_URL\|SUPABASE_ANON_KEY\|SUPABASE_SERVICE_ROLE\|supabaseUrl\|supabaseKey" \
--include="*.env*" --include="*.toml" --include="*.ts" --include="*.js" -l 2>/dev/null | head -20
Extract: SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY. If found, confirm with the user before proceeding. If not found, ask for them. Explain:
- The anon key is already public (embedded in their frontend). Sharing it reveals nothing new.
- The service_role key is needed for schema introspection (reading table structures and policy definitions). Used read-only, never stored.
- Without the service_role key, you can still run dynamic testing (Steps 3-4 only) using the anon key, but cannot inspect policy logic or generate precise fixes.
Simultaneously, scan the codebase for security red flags:
# CRITICAL: service_role key in frontend/client code
grep -rn "SERVICE_ROLE\|service_role" \
--include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" \
--include="*.vue" --include="*.svelte" -l 2>/dev/null | grep -v "node_modules\|.next\|dist\|build\|.env"
# CRITICAL: Public env var prefixes on secret keys
grep -rn "NEXT_PUBLIC_.*SERVICE\|VITE_.*SERVICE\|REACT_APP_.*SERVICE\|EXPO_PUBLIC_.*SERVICE" \
--include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" --include="*.env*" 2>/dev/null
# HIGH: Hardcoded Supabase JWTs in source files (not env)
grep -rn "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" \
--include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" 2>/dev/null \
| grep -v "node_modules\|.env"
# HIGH: .env files committed to git
git ls-files --cached .env .env.local .env.production 2>/dev/null
# MEDIUM: Supabase client initialization patterns β check for service_role in browser clients
grep -rn "createClient\|createServerClient\|createBrowserClient" \
--include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" \
-A 5 2>/dev/null | grep -v "node_modules" | head -40
Record codebase findings separately β report them even before database introspection.
Step 1 β Schema introspection
Requires the service_role key. If unavailable, skip to Step 3.
How to execute SQL β try in order:
1. Supabase MCP (if connected): Use Supabase:execute_sql tool directly. This is the easiest path.
2. Ask user to paste results: Provide the SQL, ask them to run in Dashboard β SQL Editor, paste output. Most reliable for most users.
3. Direct Postgres (if they have connection string): psql "postgresql://postgres:[pass]@db.[ref].supabase.co:5432/postgres".
Run this combined introspection query (give this to the user as one block):
-- Supabase Sentinel Introspection Query v1.0
-- Run this in your Supabase Dashboard SQL Editor and paste the results
-- 1. Table security posture
SELECT 'TABLE_STATUS' AS query, t.tablename, t.rowsecurity AS rls_enabled,
COUNT(p.policyname) AS policy_count
FROM pg_tables t
LEFT JOIN pg_policies p ON t.tablename = p.tablename AND t.schemaname = p.schemaname
WHERE t.schemaname = 'public'
GROUP BY t.tablename, t.rowsecurity
ORDER BY t.rowsecurity ASC, policy_count ASC;
-- 2. All policy details
SELECT 'POLICY' AS query, schemaname, tablename, policyname, permissive, roles, cmd,
qual AS using_expr, with_check
FROM pg_policies WHERE schemaname = 'public' ORDER BY tablename, cmd;
-- 3. Views in public schema
SELECT 'VIEW' AS query, n.nspname, c.relname AS view_name,
pg_get_userbyid(c.relowner) AS owner
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE c.relkind = 'v' AND n.nspname = 'public';
-- 4. SECURITY DEFINER functions
SELECT 'SECDEF_FUNC' AS query, n.nspname, p.proname
FROM pg_proc p JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE p.prosecdef = true AND n.nspname NOT IN ('pg_catalog','information_schema','extensions',
'auth','storage','pgsodium','vault','supabase_functions','graphql','graphql_public',
'realtime','_realtime','pgsodium_masks','pgbouncer','net','_analytics');
-- 5. Storage buckets
SELECT 'BUCKET' AS query, id, name, public FROM storage.buckets;
-- 6. Storage policies
SELECT 'STORAGE_POLICY' AS query, tablename, policyname, cmd, roles, qual, with_check
FROM pg_policies WHERE schemaname = 'storage';
-- 7. Sensitive columns
SELECT 'SENSITIVE_COL' AS query, table_name, column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'public' AND lower(column_name) IN (
'password','password_hash','secret','secret_key','api_key','api_secret',
'token','access_token','refresh_token','credit_card','card_number',
'cvv','ssn','social_security','private_key','stripe_key','openai_key');
-- 8. Functions callable by anon
SELECT 'ANON_FUNC' AS query, routine_name
FROM information_schema.routine_privileges
WHERE grantee = 'anon' AND privilege_type = 'EXECUTE'
AND routine_schema NOT IN ('pg_catalog','information_schema','extensions','auth','storage');
-- 9. Materialized views
SELECT 'MATVIEW' AS query, c.relname
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE c.relkind = 'm' AND n.nspname = 'public';
Read references/audit-queries.md for additional queries if deeper analysis is needed (policy reconstruction, mutable search paths, etc.).
Step 2 β Static analysis (anti-pattern matching)
Read references/anti-patterns.md for the complete 27-pattern database. Analyze every result from Step 1 against these checks. Be exhaustive β check every table, every policy, every function.
For each table, verify ALL of the following:
- RLS enabled? No β CRITICAL. This is the #1 cause of Supabase breaches.
- Has policies? RLS enabled + zero policies β MEDIUM (deny-all, likely a bug).
- Policies exist but RLS disabled? β CRITICAL (developer wrote policies but forgot to enable RLS β false security).
- SELECT policy permissive?
USING(true)on sensitive tables β HIGH. On public-content tables β INFO. - Write policies permissive?
USING(true)orWITH CHECK(true)on INSERT/UPDATE/DELETE β CRITICAL. - UPDATE has WITH CHECK? If USING without WITH CHECK β HIGH. Cross-reference: does the table have
is_admin,role,plan,balance,creditscolumns? If so β CRITICAL (mass assignment of privileges). - Policies scoped to roles?
roles = {public}(no TO clause) β MEDIUM, applies to anon. - Uses user_metadata?
qual/with_checkcontainsuser_metadataorraw_user_meta_dataβ HIGH. - auth.uid() wrapped? Uses
auth.uid()but not(SELECT auth.uid())β MEDIUM (performance). - Multiple permissive policies for same table/op/role? β MEDIUM (OR logic trap).
For views: No security_invoker = true? β HIGH. Bypasses all RLS on underlying tables.
For functions: SECURITY DEFINER in exposed schema? β HIGH. Callable via API, bypasses RLS. No fixed search_path? β MEDIUM.
For storage: Public buckets β MEDIUM. No storage.objects policies β HIGH.
For auth: Sensitive column names in public tables β MEDIUM. Functions callable by anon β INFO (list for review).
Step 3 β Dynamic testing (safe probing)
Safety guarantee: Prefer: tx=rollback tells PostgREST to evaluate the request fully, return the result, then roll back the transaction. Zero data modified. Safe for production.
For each table, run all four CRUD tests with the anon key:
PROJECT="SUPABASE_URL"
ANON="ANON_KEY"
TABLE="TABLE_NAME"
# SELECT
curl -s "$PROJECT/rest/v1/$TABLE?select=*&limit=1" -H "apikey: $ANON" -H "Authorization: Bearer $ANON"
# INSERT (safe rollback)
curl -s -X POST "$PROJECT/rest/v1/$TABLE" -H "apikey: $ANON" -H "Authorization: Bearer $ANON" \
-H "Content-Type: application/json" -H "Prefer: return=representation, tx=rollback" -d '{}'
# UPDATE (safe rollback)
curl -s -X PATCH "$PROJECT/rest/v1/$TABLE?id=eq.0" -H "apikey: $ANON" -H "Authorization: Bearer $ANON" \
-H "Content-Type: application/json" -H "Prefer: tx=rollback" -d '{"id":"probe"}'
# DELETE (safe rollback)
curl -s -X DELETE "$PROJECT/rest/v1/$TABLE?id=eq.0" -H "apikey: $ANON" -H "Authorization: Bearer $ANON" \
-H "Prefer: tx=rollback"
Response interpretation β be precise:
- Non-empty JSON array on SELECT β π΄ DATA EXPOSED
- Empty array [] on SELECT β β
Protected (or table empty β note ambiguity)
- "code":"42501" β β
RLS denied access
- "code":"PGRST301" β β
JWT required
- "code":"42P01" β Table doesn't exist via API (skip)
- "code":"23502" (NOT NULL violation) on INSERT β β οΈ RLS permitted the insert, but data validation failed. This is still a vulnerability β attacker just needs to provide valid column values.
- "code":"23505" (unique constraint) on INSERT β β οΈ Same β RLS permitted, constraint stopped it.
- 201 or returned data on INSERT β π΄ Anon can write
- Any successful response on UPDATE/DELETE β β οΈ Writes potentially allowed
Ghost auth test:
curl -s "$PROJECT/auth/v1/signup" -H "apikey: $ANON" -H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"Pr0beTest!2345"}'
- Response contains
"access_token"β π΄ Ghost auth active. Unconfirmed accounts get sessions. "Confirm your email"with no access_token β β Email confirmation enabled."Email signups are disabled"β β (or uses other auth providers).
If ghost auth succeeds: Re-run ALL table tests using the returned JWT instead of the anon key. This tests what an attacker with a trivially-obtained session can access, since many policies only check TO authenticated without further restrictions.
OpenAPI schema test:
curl -s "$PROJECT/rest/v1/" -H "apikey: $ANON" | head -100
If JSON with "paths" or "definitions" β π‘ Table names and column types exposed.
Step 4 β Generate the security report
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SUPABASE SENTINEL SECURITY REPORT β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β Project: [url] β
β Scanned: [date/time UTC] β
β Score: [X/100] [emoji] β
β Summary: [N] tables, [N] policies, [N] findings β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Scoring: Start at 100. Deduct: CRITICAL = -25, HIGH = -10, MEDIUM = -5. Floor at 0.
Emoji: 80-100 β
, 60-79 β οΈ, 40-59 π , 0-39 π΄.
For each finding:
[emoji] [SEVERITY] β [Table/Resource]: [Short Title]
Risk: [One sentence a non-developer understands]
Attack: [Concrete attacker scenario]
Proof: [curl command or query result that proves this]
Fix:
[exact SQL]
Ordering: CRITICAL first β HIGH β MEDIUM. Within severity, tables with likely-sensitive data first (users, payments, orders, tokens > posts, comments, settings).
End the report with:
1. "Passing" section β tables/resources that are properly secured.
2. Count summary: "X CRITICAL, Y HIGH, Z MEDIUM findings across N tables."
3. Offer: "Want me to generate a migration file with all fixes?"
4. Offer: "Want me to set up a GitHub Action for continuous monitoring?"
5. Limitation note: "This covers database/API security. It does not cover XSS, CSRF, SSRF, or infrastructure."
Step 5 β Generate fix SQL
Read references/fix-templates.md for the complete template library (8 categories, 7 policy patterns).
Policy generation rules β always follow these:
1. (SELECT auth.uid()) not auth.uid() β initPlan caching for performance.
2. Separate policies per operation β never FOR ALL.
3. Both USING and WITH CHECK on UPDATE policies.
4. Always scope with TO clause (authenticated, anon, or custom role).
5. app_metadata not user_metadata for authorization.
6. Generate indexes for policy columns.
7. Include the auto-enable RLS event trigger for future tables.
Determine the right policy pattern per table:
- Table has user_id column β ownership pattern (Pattern A in fix-templates)
- Table has team_id/org_id β team-based (Pattern B)
- Table has is_public/published β public-read + auth-write (Pattern C)
- Admin data β role-based via app_metadata (Pattern D)
- Sensitive data β verified-only (Pattern E) or MFA-enforced (Pattern F)
Ask user how to receive fixes: migration file, apply now, or step-by-step guidance.
Step 6 β GitHub Action (optional)
Read assets/github-action-template.yml. Create .github/workflows/supabase-sentinel.yml. User needs to add SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY as repository secrets. Action runs on migration changes + weekly, posts PR comments, fails on CRITICAL.
Step 7 β Preventive measures
Recommend these one-time hardening steps. Generate the SQL if the user wants:
- Auto-enable RLS event trigger β ensures future tables get RLS automatically.
- Move sensitive tables to private schema β
api_keys,secrets,internal_configshouldn't be API-exposed. - Restrict default grants β revoke INSERT/UPDATE/DELETE from anon on read-only tables.
- Enable email confirmation if not already on.
- Review OAuth redirect URLs β no wildcards in production.
- Minimum 8-char passwords with leaked password protection.
- Consider disabling Data API if app only uses Edge Functions.
- Column-level privileges on tables with sensitive columns (revoke UPDATE on is_admin, role, balance).
Reference files
Load on-demand β do not read all upfront:
references/audit-queries.mdβ Full 20-query SQL library. For additional queries beyond those inlined above.references/anti-patterns.mdβ 27 vulnerability patterns with severity, root cause, detection, Splinter lint IDs, real-world examples. Essential reading at Step 2.references/fix-templates.mdβ SQL fix templates: enable RLS, 7 RLS policy patterns (ownership/team/public-read/role-based/verified/MFA/anonymous-block), storage policies, auth hardening, function fixes, column security, migration template. Essential at Step 5.references/vibe-coding-context.mdβ CVE-2025-48757 details, 10 security studies (2025-2026), platform patterns (Lovable/Bolt/Replit/Cursor), why LLMs generate insecure code. Read when user asks "why."assets/github-action-template.ymlβ CI/CD workflow. Read at Step 6.
Principles
- Explain like a friend. Say "anyone on the internet can read your users table" not "RLS is disabled on the users relation." Explain the concrete attack scenario for every finding.
- Every finding gets a fix. Never report a problem without exact SQL to solve it.
- Safe testing only.
Prefer: tx=rollbackfor writes,.invalidTLD for auth probes. Never modify production data. - Be thorough, not alarmist. Check every table, policy, function β but calibrate severity.
USING(true)on public blog posts βUSING(true)on user payments. - Praise good security. If things are properly locked down, say so explicitly.
- State limitations clearly. This covers database/API security, not XSS, CSRF, SSRF, or infrastructure.
- Adapt to skill level. Technical user β be concise. Vibe-coder β explain RLS from scratch, walk through fixes.
# README.md
π‘οΈ Supabase Sentinel
A Claude Skill that audits your Supabase project for security vulnerabilities.
Drop it into Claude Code, Cursor, or any Claude-powered environment. Say "audit my Supabase project" and get a comprehensive security report with exact fix SQL β in minutes, not days.
170+ Lovable apps were breached. 20.1M rows were exposed across YC startups. 45% of AI-generated code introduces OWASP Top 10 vulnerabilities. Supabase's built-in Security Advisor only checks whether RLS exists β Supabase Sentinel tests whether it actually works.
What it does
Supabase Sentinel performs a 7-step security audit on any Supabase project:
- Scans your codebase for exposed service_role keys, hardcoded JWTs, and secrets committed to git
- Introspects your database schema β tables, RLS policies, views, functions, storage buckets
- Matches against 27 known vulnerability patterns sourced from CVE-2025-48757, 10 published security studies, and thousands of documented breaches
- Dynamically probes your API using the
Prefer: tx=rollbacktechnique (zero data modified, safe for production) - Tests ghost auth β can attackers create unconfirmed accounts and access your data?
- Generates a scored security report with plain-English explanations and concrete attacker scenarios
- Produces exact fix SQL β copy, paste, done
Quick start
Option 1: Claude Code / Cursor
Copy the skill folder into your project:
# Clone into your project's skills directory
git clone https://github.com/Farenhytee/supabase-sentinel.git .claude/skills/supabase-sentinel
# Or if you have a central skills directory
git clone https://github.com/Farenhytee/supabase-sentinel.git ~/claude-skills/supabase-sentinel
Then just ask Claude:
Audit my Supabase project for security issues
Claude will auto-detect your Supabase credentials from .env files, run the full audit, and present a report.
Option 2: Claude.ai (with computer use)
- Download this repo as a ZIP
- Upload it to a Claude.ai conversation with computer use enabled
- Ask: "Use the Supabase Sentinel skill to audit my Supabase project"
- Provide your Supabase URL and keys when prompted
Option 3: Manual (any AI assistant)
Copy the contents of SKILL.md into your system prompt or conversation, then follow the workflow with your Supabase credentials.
What it catches
Critical
| Pattern | Description |
|---|---|
RLS_DISABLED |
Tables without Row-Level Security β fully exposed to the internet |
SERVICE_ROLE_EXPOSED |
service_role key in frontend code β bypasses ALL security |
POLICIES_BUT_NO_RLS |
Policies written but RLS never enabled β false sense of security |
WRITE_USING_TRUE |
INSERT/UPDATE/DELETE with USING(true) β anyone can modify data |
High
| Pattern | Description |
|---|---|
USING_TRUE_SELECT |
All rows readable by anonymous users on sensitive tables |
VIEW_NO_SECURITY_INVOKER |
Views bypass RLS, running as superuser |
SECURITY_DEFINER_EXPOSED |
Functions in public schema bypass RLS, callable via API |
USER_METADATA_IN_POLICY |
Policies reference user-modifiable metadata β privilege escalation |
UPDATE_NO_WITHCHECK |
UPDATE policies without WITH CHECK β mass assignment risk |
GHOST_AUTH |
Unconfirmed email signups grant authenticated sessions |
STORAGE_NO_RLS |
Storage bucket missing access control policies |
JWT_SECRET_EXPOSED |
JWT signing secret leaked β can forge any user's token |
Medium
| Pattern | Description |
|---|---|
RLS_NO_POLICIES |
RLS enabled but no policies β all access silently denied (bug) |
POLICY_NO_ROLE_SCOPE |
Policy applies to all roles including anonymous |
MULTIPLE_PERMISSIVE |
Multiple permissive policies OR'd β most permissive wins |
RLS_PERFORMANCE |
auth.uid() not cached β performance degradation, potential DoS |
PUBLIC_BUCKET |
Storage bucket publicly accessible without auth |
SENSITIVE_COLUMNS |
Columns named password, api_key, etc. exposed via API |
| + 9 more patterns | See references/anti-patterns.md for the full list |
Example output
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SUPABASE SENTINEL SECURITY REPORT β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β Project: https://myapp.supabase.co β
β Scanned: 2026-03-15 14:30 UTC β
β Score: 35/100 π΄ β
β Summary: 12 tables, 8 policies, 7 findings β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π΄ CRITICAL β users: RLS Disabled
Risk: Anyone on the internet can read your entire users table
Attack: Open browser DevTools β copy anon key β curl the API β dump all emails, names, metadata
Proof: curl returns [{"id":"...","email":"[email protected]",...}]
Fix:
ALTER TABLE public.users ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_select_own"
ON public.users FOR SELECT TO authenticated
USING ((SELECT auth.uid()) = id);
π HIGH β profiles: SELECT policy uses USING(true)
Risk: All user profiles are readable by anyone, including anonymous users
Attack: Enumerate all profiles via the API to harvest user data
...
β
PASSING: orders, payments, invoices, subscriptions (4 tables properly secured)
File structure
supabase-sentinel/
βββ SKILL.md # Core skill β 7-step audit workflow (333 lines)
βββ references/
β βββ audit-queries.md # 20 SQL queries for schema introspection
β βββ anti-patterns.md # 27 vulnerability patterns with severity/detection/fix
β βββ fix-templates.md # SQL fix templates β 7 RLS patterns, storage, auth, prevention
β βββ vibe-coding-context.md # CVE-2025-48757, research studies, platform analysis
βββ assets/
β βββ github-action-template.yml # CI/CD workflow for continuous monitoring
βββ README.md
βββ LICENSE # MIT
How progressive disclosure works: When Claude loads this skill, it only reads the 333-line SKILL.md initially (~5000 tokens). Reference files are loaded on-demand during specific audit steps β audit-queries.md at Step 1, anti-patterns.md at Step 2, fix-templates.md at Step 5. This keeps context usage efficient.
Continuous monitoring (GitHub Action)
Supabase Sentinel can generate a GitHub Action that:
- Runs on every push to supabase/migrations/
- Runs weekly (Monday 6am UTC)
- Posts findings as PR comments
- Blocks merges on CRITICAL findings
Just ask: "Set up continuous security monitoring for this project."
See assets/github-action-template.yml for the template.
Research backing
This skill's anti-pattern database is sourced from:
- CVE-2025-48757 β 170+ Lovable apps exposed, CVSS 9.3 (Matt Palmer, May 2025)
- Escape.tech β 2,000+ vulnerabilities across 5,600 vibe-coded apps (October 2025)
- Veracode β 45% of AI-generated code introduces OWASP Top 10 vulnerabilities (July 2025)
- Carnegie Mellon SusVibes β 82.8% of functionally correct AI code was insecure (December 2025)
- SupaExplorer β 11% of indie apps expose Supabase credentials (January 2026)
- ModernPentest β 20.1M rows exposed across 107 YC startups (March 2026)
- Wiz Research β Critical auth bypass in Base44 vibe-coding platform (July 2025)
- Supabase Security Retro 2025 β Official documentation of built-in advisor capabilities and gaps
- Supabase Splinter β All 16 official security lints mapped and extended
See references/vibe-coding-context.md for the full research breakdown.
What Supabase Sentinel catches that Supabase Security Advisor doesn't
Supabase's built-in Security Advisor (Splinter) runs 16 lints. Supabase Sentinel extends this with:
| Gap | What Splinter misses | Supabase Sentinel covers |
|---|---|---|
| Policy correctness | Only checks if policies exist | Tests if they actually prevent unauthorized access |
| Dynamic probing | Static analysis only | Live API testing with tx=rollback |
| Ghost auth | Not checked | Tests email confirmation bypass |
| Mass assignment | Not checked | Detects UPDATE without WITH CHECK + sensitive columns |
| Storage config | Not checked | Audits bucket visibility and storage.objects RLS |
| Codebase scanning | Not applicable | Finds service_role keys in frontend code |
| Key exposure | Not checked | Detects hardcoded JWTs and committed .env files |
| Column-level security | Not checked | Flags sensitive columns accessible via API |
| CI/CD integration | Not available | GitHub Action for continuous monitoring |
Contributing
Contributions are welcome! The most valuable contributions are:
- New anti-patterns β Found a Supabase security issue not in our database? Add it to
references/anti-patterns.mdwith severity, detection query, fix SQL, and real-world evidence. - Fix template improvements β Better RLS policy patterns, edge cases, or performance optimizations in
references/fix-templates.md. - Testing on real projects β Run Supabase Sentinel on your own Supabase projects and report false positives/negatives.
- Platform-specific patterns β Document security patterns specific to Lovable, Bolt, Replit, or other vibe-coding platforms.
How to contribute
- Fork this repo
- Create a branch (
git checkout -b add-new-pattern) - Add your changes with clear documentation
- Submit a PR with a description of the pattern and evidence
Roadmap
- [ ] CLI tool β
npx supabase-sentinel auditfor non-Claude environments - [ ] MCP server β programmatic access for CI/CD and dashboards
- [ ] Firebase support β extend to Firebase Security Rules auditing
- [ ] Premium dashboard β historical trending, multi-project views, Slack alerts
- [ ] VS Code extension β inline security warnings in the editor
Safety
Supabase Sentinel is designed to be safe for production use:
- Dynamic tests use
Prefer: tx=rollbackβ PostgREST processes the request, evaluates RLS, returns the result, then rolls back. Zero data modified. - Auth probes use
.invalidTLD β test emails use RFC 2606 reserved domains that cannot receive mail. - Read-only introspection β schema queries only read
pg_tables,pg_policies, andinformation_schema. No DDL or DML. - Open source β audit the auditor. Every query and test is visible in the source.
License
MIT β use it however you want, commercially or otherwise.
Built for the vibe-coding era.
Because "it works" and "it's secure" are two very different things.
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.