The Supabase deploy-gate that runs where you vibe-code — block the RLS and secret leaks before deploy.
Project description
rlsgate
The Supabase deploy-gate that runs where you vibe-code — and blocks only on the holes that are truly dangerous.
In 2026, a wave of AI-built ("vibe-coded") apps shipped to production with the same handful of security holes. CVE-2025-48757 and the Lovable disclosure cataloged 170+ live apps leaking user PII — most through one anti-pattern: a Row Level Security policy that lets any logged-in user read everyone's rows. Studies of vibe-coded apps found ~80% repeat the same RLS mistake and ~72% ship a hardcoded secret somewhere.
rlsgate is a local-first, static scanner that reads your Supabase migrations, RLS policies, env files, and frontend bundle and blocks the deploy when it finds the finite set of high-signal holes that actually leak. No database connection, no telemetry, no account. It runs in seconds, as a CLI, a Claude Code skill, or a GitHub Action.
$ rlsgate scan
rlsgate · 1 migrations, 1 env, 2 source files scanned
✖ CRITICAL Table public.profiles holds personal data but Row Level Security is never enabled
where: supabase/migrations/0001_init.sql:2
why: With RLS off in a client-reachable schema, anyone holding the public anon key can read every row.
fix: ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; then add an ownership policy.
✖ CRITICAL RLS policy 'read all posts' grants every authenticated user access to public.posts
where: supabase/migrations/0001_init.sql:15
fix: Bind the policy to the row owner, e.g. USING (auth.uid() = user_id).
▲ HIGH Webhook handler does not verify the request signature (app/api/stripe/webhook/route.ts)
...
3 critical 2 high
$ echo $?
1
Why it's different
Plenty of tools scan vibe-coded apps. rlsgate is the one that is local-first, runs inside your build loop, and blocks at deploy — not a dashboard you visit, not a sidecar you run in production.
- vs hosted SaaS scanners — they guess at your security from outside your live URL and want a token to your repo.
rlsgatereads your actual migrations and RLS policies locally. No DB connection, no account, no token handoff, no telemetry. - vs runtime sidecars / firewalls — they filter requests after deploy, in front of your app.
rlsgatecatches the hole before deploy, in your repo, where it's a one-line fix instead of an incident. - vs broad scanners — they compete on rule count (hundreds to thousands), which means noise, which means you turn them off.
rlsgateships a finite, high-precision set of checks for the holes that actually leak — including the CVE-2025-48757 "any authenticated user can read any row" class that breadth scanners miss — so it's a gate you'll actually leave on.
It's the same checks across three surfaces with full parity: a CLI, a Claude Code skill that fires mid-build (inside the AI tool generating your app), and a GitHub Action that blocks the merge. Works across any builder — Lovable, Bolt, Cursor, v0, or hand-rolled.
Install
pip install rlsgate # or: uv tool install rlsgate
rlsgate scan # scan the current directory
No Python project? A zero-install run:
uvx rlsgate scan ./my-app
Usage
rlsgate scan [PATH] # scan a project (default: .)
rlsgate scan --json # machine-readable output
rlsgate scan --fail-on critical # only CRITICAL fails the gate (default: high)
rlsgate scan --ai # add AI-drafted fix snippets (needs ANTHROPIC_API_KEY)
rlsgate rules # list the checks
Exit codes: 0 clean (or only findings below --fail-on) · 1 blocking findings · 2 usage error. The non-zero exit is what makes it a gate.
What it checks (v1)
| Rule | Severity | The hole |
|---|---|---|
rls-authenticated |
CRITICAL | RLS policy authorizes any authenticated user instead of the row owner — the CVE-2025-48757 class |
rls-disabled |
CRITICAL/HIGH | A user/PII table created without ENABLE ROW LEVEL SECURITY (wide open to the anon key) |
anon-access |
CRITICAL/HIGH | Data reachable by the unauthenticated anon role via a permissive policy or grant |
public-bucket |
HIGH | A Supabase Storage bucket created public (every object served to anyone with the URL) |
exposed-secret |
CRITICAL/HIGH | A real secret committed to .env, or a service-role key / sk- key shipped to the browser bundle |
unverified-webhook |
HIGH | A webhook endpoint that acts on the body without verifying the provider's signature |
The Supabase anon key (a public JWT with role: anon) is intentionally public and is never flagged — only the dangerous service_role key is.
GitHub Action
# .github/workflows/rlsgate.yml
name: rlsgate
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: GerardoRdz96/rlsgate@v0.1.0
with:
path: .
fail-on: high
Claude Code skill
Copy skill/ into your project's .claude/skills/rlsgate/ (or install the repo as a plugin). Then, in the agent loop before deploy:
/rlsgate
The skill runs the gate and brings the findings into the conversation so the agent fixes them before shipping.
What rlsgate is NOT
- Not a full security audit. It checks a finite, high-signal ruleset, statically. It will not catch logic bugs, business-rule auth flaws, or holes that only appear at runtime.
- Not a database scanner. v1 reads files only — it never connects to your Supabase project. A policy that looks correct but behaves differently at runtime is out of scope.
- Not an auto-fixer. The deterministic finding is the source of truth. The optional
--aifix is clearly labeled AI-drafted and must be reviewed before you apply it.
A clean rlsgate run means "none of the known high-signal holes are present," not "this app is secure."
License
MIT © 2026 Penguin Alley
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rlsgate-0.1.0.tar.gz.
File metadata
- Download URL: rlsgate-0.1.0.tar.gz
- Upload date:
- Size: 25.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54e0994940578fb4901f6f4de3a4e5a0057252863e3207712d6cb49f23d4471e
|
|
| MD5 |
80c1c28ba96cee9080c041e0faed059a
|
|
| BLAKE2b-256 |
f6aaf367f354c55763f0ddcdf2cb8e5a0f35778377f722a4d08dbddacde514f2
|
File details
Details for the file rlsgate-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rlsgate-0.1.0-py3-none-any.whl
- Upload date:
- Size: 33.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e2567dbef8c931b873405681ff56c573ead8e990f7ef8823475bf04a45d25c4
|
|
| MD5 |
ca6b97ac7d862d28b2946d85ed4de19b
|
|
| BLAKE2b-256 |
0d009d34b1f6d1b703278be65e92baa2cd580e37c1312c3a5d9cfa5a4b8f49e7
|