For engineers
You approve the change. Here is everything you need to say yes.
Each fix is a migration, re-tested in a rebuilt copy of the database as an anonymous visitor, a signed-in stranger and the owner. You get the SQL, the before-and-after counts, a risk note and a signed receipt. Nothing changes until you apply it.
Fix · PL-001
public.profiles → owner-only read
supabase/migrations/20261109165700_patchlabs_leak_fix.sql
-- Fix: replace "everyone can read" with owner-only access DROP POLICY "Profiles are viewable by everyone" ON public.profiles;CREATE POLICY "Users can view own profile" ON public.profiles FOR SELECT USING (auth.uid() = user_id);
What you review
Evidence, in the formats you already use.
A migration, not advice
A timestamped file for supabase/migrations, with comments on what changed and why. Download it, or commit it on GitHub.
Counts for each identity
Rows read before and after, as anon, a signed-in stranger and the owner, on a database rebuilt from your migrations.
A signed receipt
Ed25519-signed: the result, the fix’s hash and every decision your team made. Verifiable by anyone with the public key.
How the proof runs
The exact identities, simulated inside a disposable database.
The lab applies your migrations to a local Postgres with Supabase’s roles, auth.uid() and default grants, seeds fake rows for two users, then sets the role and JWT claims for each identity.
- Never connects to your project; we never need a password or service key
- The smallest model writes first; spend capped per check
- A fix that fails any identity goes to a person, never shipped as proven
-- 1. anonymous visitor, with your public key
set local role anon;
select count(*) from public.profiles; -- 2: LEAK
-- 2. signed-in stranger
set local role authenticated;
select set_config('request.jwt.claims', '{"sub":"<stranger>"}', true);
select count(*) from public.profiles; -- 2: LEAK
r = get(f"{API}/receipts/{check_id}")
key = get(f"{API}/.well-known/patchlabs-receipt-key")
msg = json.dumps(r["receipt"], sort_keys=True,
separators=(",", ":")).encode()
Ed25519PublicKey.from_public_bytes(
b64decode(key["public_key"])).verify(
b64decode(r["signature"]), msg)
In your workflow
From your terminal, your agent or your CI.
Approve or mark intended
Record decisions on each check; suppressions stop repeat alerts. Decisions.
Pull requests
Fixes as pull requests on your repo, and private repos. Coming.
What does the lab not model?
Anything outside your migrations: changes made by hand in the dashboard, extensions or settings not in the repo, and backends other than Supabase/Postgres. The report lists what it couldn’t rebuild.
Does it understand intentionally public tables?
Mark them as intended once; later checks keep reporting them but stop alerting. Decisions go into the signed receipt.
Can I see the raw findings?
Yes. Every finding has the same JSON shape across the report, API and webhooks: ID, severity, object, file, line, evidence, proof and fix.
Is this a pentest?
No. It tests specific, documented things in depth. Use it alongside a pentest for a high-stakes launch.
Free check
Run it on a repo you know well.
The fastest way to judge the proof is on code you understand. First app free.