June 26, 2026 · 9 min read
The Sanity Layer: Security Audits for AI-Generated Code
AI optimizes for working, not safe. Open RLS, leaked API keys, unverified webhooks — the audit checklist for every vibe-coded app before it sees a real user.
By Launched team
The agent shipped a working app. "Working" and "safe to put on the internet" are different sentences. We've audited dozens of AI-generated codebases and the same handful of vulnerabilities shows up in nearly every one — not because the agent is malicious, but because security has no visible feedback loop. Open RLS doesn't show up in the UI. A leaked API key doesn't crash the build.
The greatest hits of AI-generated insecurity
1. Wide-open Row Level Security
The most common pattern: RLS is enabled (good), but the policy is USING (true) (catastrophic). Every row, readable and writable by anyone with the anon key — which is in your client bundle. Effectively no auth at all.
2. Service role key in the client
The agent needed a "more powerful" key to make a feature work and grabbed the service role. It bypasses RLS entirely. If it's in the browser, your entire database is public.
3. API keys hardcoded in source
OpenAI, Stripe, Resend — pasted into a server file and committed. Even after you rotate, the old key lives in git history forever.
4. Webhooks with no signature verification
Stripe webhook endpoint accepts any POST and updates user subscriptions. An attacker hits it with a forged payload and promotes themselves to lifetime pro.
5. Role stored on the profile row
An is_admin column on a row the user themselves can update. Classic privilege escalation. Roles must live in a separate table the user cannot write.
6. Server functions with no auth check
An RPC called deleteAccount that takes a user ID parameter and trusts it. Anyone can delete anyone.
7. CORS set to * on storage buckets
Upload endpoints accept files from any origin. Hello, hotlinked storage bill.
The 20-minute audit you can run today
- Grep your repo for
SERVICE_ROLE. It should not appear in any client-imported file. - Grep for hardcoded keys (
sk_,re_,sb_secret_). Move to env, rotate. - Open every RLS policy. Reject any with
USING (true). - Confirm every webhook handler verifies a signature before doing anything.
- Confirm every server function checks
auth.uid()matches the row being modified. - Confirm roles live in a separate
user_rolestable with its own RLS.
Where Launched comes in
We run a Sanity Layer pass on AI-generated apps before they take real users. Two days, fixed price, written report plus the patches. Book a 20-minute call. Related: AI Visibility Audit.
