Security scanner for Supabase and Lovable projects. Audits Row Level Security (RLS) policies by analyzing SQL migrations locally or querying live databases remotely. Detects misconfigurations, missing protections, and common security anti-patterns.
Vibe-Scanner reads your Supabase migration files (or connects directly to your live database), builds the final state of every table's RLS configuration, and runs 62 detection rules against it. It catches issues like:
- Tables with RLS enabled but no policies defined
- Policies using
USING(true)or other tautologies (NOT FALSE,1=1) - UPDATE policies missing
WITH CHECKclauses - Missing
FORCE ROW LEVEL SECURITY SECURITY DEFINERfunctions withoutsearch_pathreset- Views missing
SECURITY INVOKER GRANT TO PUBLICon tables with RLS- Roles with
BYPASSRLSprivilege - Realtime publications on tables without restrictive RLS
- Hardcoded secrets in
config.toml
Vibe-Scanner is a read-only auditing tool. It detects and reports, but it does not modify your database, apply fixes, or patch your code. If you find yourself vulnerable, we're temporarily giving free access to Symbiotic Code so you can remediate. Not a sales trick, just trying to be helpful without having to carve out a new tool from our product.
uv sync# Console output (Rich-formatted, grouped by table)
uv run rls-scanner ./path/to/supabase/
# JSON report
uv run rls-scanner ./path/to/supabase/ -o json -f report.json
# Markdown report
uv run rls-scanner ./path/to/supabase/ -o markdown -f report.md
# Custom rules file
uv run rls-scanner ./path/to/supabase/ -r custom_rules.yaml
# Visual graph outputs
uv run rls-scanner ./path/to/supabase/ -o mermaid-er # ER diagram
uv run rls-scanner ./path/to/supabase/ -o mermaid-security # Security flow
uv run rls-scanner ./path/to/supabase/ -o graph-json # Machine-readable graphScan a live Supabase database directly using a personal access token. This finds more vulnerabilities than local scanning because it sees the actual deployed state (storage tables, system roles, SECURITY DEFINER functions).
# Basic remote scan
uv run rls-scanner -t sbp_xxxxx -p <project_ref>
# JSON output
uv run rls-scanner -t sbp_xxxxx -p <project_ref> -o json -f report.json
# Scan a specific schema
uv run rls-scanner -t sbp_xxxxx -p <project_ref> --schema public
# Using environment variables
export SUPABASE_ACCESS_TOKEN=sbp_xxxxx
export SUPABASE_PROJECT_REF=yitwhbimoqlwfridlxak
uv run rls-scannerScan Supabase databases managed by Lovable via their internal DB proxy. The Supabase ref is auto-discovered from the Lovable API.
# Scan a single project (supabase ref auto-discovered)
uv run rls-scanner \
--lovable-token "eyJxxx..." \
--lovable-project "40d72bc5-5e42-4b7d-aba4-15e37e5433fc"
# Also accepts the full Lovable URL
uv run rls-scanner \
--lovable-token "eyJxxx..." \
--lovable-project "https://lovable.dev/projects/40d72bc5-5e42-4b7d-aba4-15e37e5433fc"
# Scan ALL projects in your workspace at once
uv run rls-scanner --lovable-token "eyJxxx..." --lovable-scan-all
# JSON output
uv run rls-scanner \
--lovable-token "eyJxxx..." \
--lovable-project "40d72bc5-..." \
-o json -f report.json
# Using environment variables
export LOVABLE_TOKEN="eyJxxx..."
export LOVABLE_PROJECT_ID="40d72bc5-5e42-4b7d-aba4-15e37e5433fc"
uv run rls-scanner
# Scan all projects with env variable
export LOVABLE_TOKEN="eyJxxx..."
uv run rls-scanner --lovable-scan-all
# Override supabase ref manually (skips auto-discovery)
uv run rls-scanner \
--lovable-token "eyJxxx..." \
--lovable-project "40d72bc5-..." \
--supabase-ref "rscgeiiibodxpqpwojfz"How to get the Lovable token:
- Open https://lovable.dev in Chrome
- Open DevTools (F12) > Network tab
- Filter requests to
api.lovable.dev - Copy the
Authorization: Bearer eyJ...header from any request Then run:
uv run rls-scanner --lovable-token "eyJxxx..." --lovable-scan-all
uv run rls-scanner --lovable-token "eyJxxx..." --lovable-project "40d72bc5-..."| Local scan | Remote scan (Supabase / Lovable) | |
|---|---|---|
| Source | SQL migration files | Live database (pg_class, pg_policies, pg_proc, etc.) |
| Storage tables | Not visible | storage.objects, storage.buckets scanned |
| System roles | Not visible | BYPASSRLS roles detected (e.g. service_role) |
| SECURITY DEFINER | Only if in migrations | All functions from pg_proc |
| Realtime check | Only if ALTER PUBLICATION in SQL |
Queries pg_publication_tables directly |
The scanner exits with code 1 if any CRITICAL or HIGH findings are detected, making it usable in CI pipelines.
SQL files (sorted by name = migration order)
|
v
+---------------------------------------------+
| SQL Parser (regex + pglast AST) |
| Extracts: tables, policies, grants, roles, |
| functions, views, realtime subscriptions |
+-----------------------+---------------------+
| Mutations applied in order
v
+---------------------------------------------+
| Migration State |
| Temporal tracking: CREATE, ALTER, DROP |
| Final state per table: RLS on/off, |
| policies, FORCE RLS, columns |
+-----------------------+---------------------+
| One RLSTableConfig per table
v
+---------------------------------------------+
| Vulnerability Detectors (per table) |
| + Cross-cutting checks (grants, roles, |
| realtime, config.toml) |
+-----------------------+---------------------+
| Findings deduplicated
v
+---------------------------------------------+
| Reporter (console / JSON / markdown) |
+---------------------------------------------+
The scanner uses two complementary parsers:
| Parser | Technology | Role |
|---|---|---|
| SQLParserAdapter | Regex patterns | Main parser. Extracts tables, policies, grants, roles, functions, views, triggers, and realtime publications from SQL. Fast and handles Supabase-specific syntax. |
| PostgreSQLASTParser | pglast v7 (libpg_query) |
Parses policy expressions (USING, WITH CHECK) into a real PostgreSQL AST. Detects auth function calls, tautologies, subqueries, operators, column/table references with high accuracy. Falls back to regex if pglast is unavailable or the expression cannot be parsed. |
The parsing is temporal: migration files are processed in sorted order. CREATE POLICY, DROP POLICY, ENABLE RLS, DISABLE RLS are tracked as mutations. The scanner analyzes the final state after all migrations have been applied.
Detection is split across specialized detectors, each responsible for a category of vulnerabilities:
| Detector | Rules | What it checks |
|---|---|---|
| VulnerabilityDetector | RLS-001 to RLS-007 | RLS enabled, policies exist, missing CRUD operations |
| PolicyStructureChecker | RLS-010 to RLS-015, RLS-024, RLS-026, RLS-049 | Clause issues (INSERT without WITH CHECK, UPDATE without WITH CHECK), role issues (service_role), OR without auth, FOR ALL policies |
| UsingClauseChecker | RLS-008, RLS-016 to RLS-023, RLS-025, RLS-027 to RLS-030, RLS-047, RLS-048 | Auth checks in USING clause, tautologies, subquery patterns, operator issues, non-existent columns, infinite loops |
| WithCheckChecker | RLS-008, RLS-018, RLS-025, RLS-057 | Auth checks in WITH CHECK clause, tautologies, custom functions |
| ColumnAccessDetector | RLS-031 to RLS-033, RLS-050 | Sensitive column exposure, missing column-level restrictions |
| SecurityDefinerDetector | RLS-034 to RLS-040 | SECURITY DEFINER functions: missing search_path, missing auth, dynamic SQL, data exposure |
| ViewSecurityDetector | RLS-041, RLS-044, RLS-045 | Views missing SECURITY INVOKER, views using SECURITY DEFINER |
| Cross-cutting checks | RLS-051 to RLS-062 | DEFINER/view usage in policies, FORCE RLS, GRANT TO PUBLIC, BYPASSRLS roles, realtime tables, config.toml issues |
All 62 rules are defined in config/rules.yaml and can be customized.
Every rule ID is declared in src/domain/rules/rule_ids.py as a Rule(str, Enum), grouped by category.
| Level | Meaning |
|---|---|
| CRITICAL | Immediate fix required — data is exposed (e.g. USING(true)) |
| HIGH | Significant security gap (e.g. missing FORCE RLS, no auth check) |
| MEDIUM | Moderate risk (e.g. subquery joining source table, missing operation) |
| LOW | Minor gap (e.g. missing DELETE policy) |
| WARNING | Best practice recommendation (e.g. custom function without verification) |
src/
├── main.py # CLI entry point (Click)
├── main_helpers.py # Shared scan result helpers
├── main_lovable.py # Lovable scan entry points
├── domain/ # Domain layer (no external dependencies)
│ ├── models/ # Pydantic models, dataclasses, enums
│ ├── ports/ # Abstract interfaces (parser, detector, remote DB)
│ └── rules/ # Rule loader, rule IDs enum, finding factory
├── application/ # Application layer (orchestration)
│ ├── services/ # ScannerService, remote scanner, cross-cutting checks
│ └── use_cases/ # ScanUseCase entry point
└── infrastructure/ # Infrastructure layer (adapters)
├── parsers/ # SQL regex parser, pglast v7 AST, regex fallback
├── detectors/ # All vulnerability detectors
├── reporters/ # Console (Rich), JSON, Markdown, Mermaid, Graph
└── remote/ # Supabase API adapter, Lovable API client
config/
└── rules.yaml # 62 vulnerability rules (YAML)