RESEARCH · SOURCE-ONLY SWEEP · JUNE 2026

What breaks first in vibe-coded Supabase apps — and why your scanner can't tell you

We manually reviewed the SQL migrations of 1,136 public repos. The most-flagged "critical" pattern was more than twice as likely to be safe as dangerous — and among the real holes, 83% were the same mistake: personal data left readable by anyone with the app's public key.

Method: source-only static review of public GitHub repos with Supabase migrations. No app was touched, logged into, or attacked — every finding is read from committed SQL. A further ~370 repos were screened in a second pass. Numbers are deliberately conservative (see Limits).

1,136
MIGRATION FILE SETS SCREENED, PASS 1
42
"DANGEROUS-LOOKING" POLICIES — SAFE BY DESIGN
20
REAL P0/P1 HOLES, CONFIRMED FROM SOURCE
~83%
OF CONFIRMED HOLES = PII LEFT WORLD-OPEN

TL;DR — three findings

1 · The flaw is overwhelmingly one thing: PII tables left world-open. Of 48 real access-control holes confirmed across both passes, ~83% were an unscoped USING (true) / WITH CHECK (true) policy on a profiles / users table — readable (and usually writable) by anyone with the public anon key. Financial (orders) and HR (employees) tables showed up too, but rarely in open source.

2 · Most using(true) alarms are NOT bugs — and a scanner can't tell. In Pass 1 we manually cleared 42 using(true) policies as safe by design (scoped TO service_role, which bypasses RLS anyway). That's more than 2× the 20 real holes in the same sweep. A string-matching scanner flags all 62 identically and ranks none. Telling the safe ones from the lethal ones is the entire job.

3 · The builder is rarely fingerprintable from source — and it doesn't matter. Across the leads we sourced, the AI tool was usually undetectable (everything compiles down to a Next.js/Vite app). Where it was visible — Bolt/StackBlitz, Cursor — the same flaw appeared. This is a builder-agnostic default, not a Lovable problem or a Bolt problem.

What breaks first — by class (48 confirmed holes)

Vulnerability classShareWhat an attacker gets
PII exposure (profiles/users/customers)~83%Read/rewrite every user's personal data from the browser
Financial data (orders/payments)~6%Read/modify order & payment rows
Generic data tables (clients/subscriptions)~4%Read/modify business records
Captured contacts (contacts)~2%Harvest every contact-form submission
Regulated PII (employees/HR)~2%Read employee records — possible regulatory weight
Privilege escalation (role/admin table)~2%Self-promote to admin

The long tail (priv-esc, financial, regulated) is rarer but higher severity — and exactly the kind a generic scanner buries under hundreds of low-value flags.

The false-alarm tax — the headline most people get wrong

Every using(true)-shaped policy found in Pass 1 (n = 1,136), by manual verdict:

Outcome of a using(true) policyCountVerdict
Scoped TO service_role (or postgres/admin role)42SAFE BY DESIGN — service_role bypasses RLS; default-deny for everyone else
Unscoped, on a sensitive table20REAL P0/P1 — reaches anon/authenticated
No actual (true) policy (string-match noise)13NOT APPLICABLE

Read it again: in this sweep, a using(true) policy on a data table was more likely to be safe than dangerous. The 10-second test that separates them: does the policy carry a TO service_role clause? Yes → almost certainly fine. No TO clause (or TO public/authenticated) → that's the hole.

By builder (where detectable, 25 sourced leads)

Signal in sourceCountNote
Next.js / Vite (no builder fingerprint)21Most AI builders emit this — tool not identifiable from source
Bolt / StackBlitz ("Created with StackBlitz")2Same flaw
Cursor (.cursor/)2Same flaw

Honest takeaway: you cannot reliably tell which AI tool wrote the code from the repo, and it doesn't change the finding. The vulnerability is in how access control is reasoned about, not which tool typed it.

Limits (so you can trust the rest)

READ THIS BEFORE QUOTING THE NUMBERS

Open-source sample bias. Public repos skew toward pet projects, portfolios and early MVPs — fintech/health/payments are under-represented here vs. what ships privately. The PII dominance is real; the rarity of regulated data is a sampling artifact, not reassurance.

Static, source-only. Findings are read from migrations, not confirmed against a running database. Each is framed as "confirm exact impact in a full review."

The false-alarm ratio is directional, not a precise population rate: "safe" counts service_role scoping on any table; "real hole" counts unscoped policies on sensitive tables. Different denominators — reported as observed counts, not a clean percentage.

Disclosure. Where a flaw was real, the owner was notified privately (responsible disclosure). Findings here are class-level only — no named verdicts.

Why judgment is the product

A scanner greps using(true) and hands you 200 undifferentiated flags. This sweep is the opposite: a senior read that says this one is fine, this one breaches you, here's the file:line and the one-line fix. That judgment — clearing the 42 and catching the 20 — is the entire difference between coverage and a verdict.

WHAT WE DO WITH THIS METHOD

Written, async security & readiness reviews — every finding cited at file:line, every boundary named, responsible disclosure always.