
AI coding tools can ship a full-stack app in an afternoon. I wanted to know what they ship with.
So I took 200 public GitHub repos of apps built with Lovable, Bolt, v0 and Supabase + Next.js
templates (about 50 of each, all updated in 2025–2026), and ran an offline static security scan on
every one. No live sites were touched, no repo is named, and only aggregate numbers are reported.
The headline
48% of the Supabase apps (43 of 90) had at least one serious database exposure:
| Problem (Supabase apps, n = 90) | Share |
|---|---|
An Edge Function anyone can call (verify_jwt = false, no auth in its code) that uses the service-role key, which bypasses Row Level Security |
27% |
A Row Level Security policy that lets anyone read or write rows with private data (USING (true) on writes, or on tables with emails, phones, payments…) |
24% |
| A table in the public schema with Row Level Security off | 19% |
Why this matters: in a Supabase app the anon key ships to every browser by design. The RLS
policies (and your Edge Functions' own checks) are the only thing between a visitor and your rows.
Other things that showed up a lot
-
38% of Supabase apps had
SECURI TY DEFINERfunctions that any signed-in user can call. Some check roles inside, many don't — one wallet function took the user id to charge as a parameter. - 23% of all apps ran a Next.js / React version with a published critical advisory.
- 23% of all apps set no security headers at all (no CSP, no frame protection).
- 31% of Supabase apps had a public storage bucket (often intended — worth a look anyway).
What I did not count
Static analysis is noisy if you let it be. Before publishing, I hand-checked samples from every
category against the real code, and dropped or fixed what didn't hold up:
- Categories I left out of the headline because spot-checks showed mixed precision: API routes with no auth check, XSS sinks, injection. They're real sometimes — not often enough to quote a number.
- Doing this found 10 false-positive patterns in my own scanner (React Router
navigate()treated as an open redirect, digits-only values treated as injectable, deliberately public tables rated "high", custom guards likerequireOrganizer()not recognised, a second service's SQLite migrations treated as Supabase…). All fixed, each with a regression test, in v0.3.1–0.3.2.
Method
- Repos: GitHub search on README markers ("Welcome to your Lovable project", lovable.dev, bolt.new, v0.dev) and Supabase + Next.js TypeScript apps; non-forks, pushed since 2025-01-01, under 60 MB; interleaved by source; 200 scanned.
- Scanner: whitehat-squad 0.3.2, offline, with each repo's own config ignored; low-confidence heuristics excluded.
- These are static findings, not confirmed exploits. A finding says "this code/config allows X", not "someone did X".
- Owners of the most serious confirmed issues are being notified privately.
Check your own app (free, offline, ~1 second)
npx whitehat-squad scan .
It explains each problem in plain English, writes the fix (one merged SQL migration for the
database issues, plus a prompt you can paste into Claude Code / Cursor / Codex), and
whsquad verify <rule> proves the hole is closed. 185 rules, no API key, Apache-2.0.
If it flags something that's actually fine, please open a false-positive issue — that's exactly how the ten above got fixed.
Top comments (0)