All posts

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_roles table 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.