VibeRaven

What are the security risks of vibe coding?

Vibe coding does carry real security risk, and it sits in a handful of places: Supabase tables without working RLS, secret keys shipped in the client bundle, Stripe webhooks that do not verify signatures, security definer functions anyone can call, .env files committed to git, and routes with no rate limit. Each one can be found with a query or a search and fixed in a few lines, and each section below shows how.

Why this matters

A coding agent writes code that makes the feature work, and most of these risks never show up as a bug: the app behaves the same whether a table has RLS or not, and whether a webhook checks its signature or not. They show up only when someone outside calls your API directly. That is also why they repeat at scale: the CVE-2025-48757 researchers found missing or weak RLS across 170 projects from one platform's showcase, and UpGuard found 16,326 Supabase databases with readable tables.

The six risks, by how much they expose

  • Open RLS: anyone with your public key can read or change a table.
  • Leaked keys in client bundles: a secret key in the browser skips every rule you set.
  • Unsigned webhooks: anyone can fake a payment event.
  • Security definer functions: a database function runs with its owner's rights for any caller.
  • .env in git: keys live on in history after you delete the file.
  • Missing rate limits: one script can run up your AI, email or database bill.

Each risk: what it is, how to detect it, how to fix it

The SQL was run on Postgres before publishing; swap in your own table and function names.

Open RLS

What it is: a table in the public schema with row level security off, or on with a policy whose condition is a bare true. Supabase's docs say a table in an exposed schema without RLS "is readable and writable by any role with a grant on it", and the publishable key in your page source is enough to send the request. This is the mechanism behind CVE-2025-48757 and the UpGuard findings.

Detect it by listing public tables with RLS off and policies whose condition is true. Fix it by enabling RLS and writing a policy for each action that compares the row's owner with auth.uid().

-- tables with RLS off
select tablename from pg_tables
where schemaname = 'public' and rowsecurity = false;

-- policies that let everyone through
select tablename, policyname, cmd from pg_policies
where schemaname = 'public' and (qual = 'true' or with_check = 'true');

-- the fix, per table and action
alter table public.notes enable row level security;
create policy "Owners read their notes" on public.notes
for select to authenticated
using ( (select auth.uid()) = user_id );

Go deeper: How do I find Supabase tables missing RLS in my migrations?

Go deeper: What does using (true) mean in a Supabase RLS policy, and when is it wrong?

Leaked keys in client bundles

What it is: a key that grants more than the public key, built into JavaScript the browser downloads. Next.js inlines every NEXT_PUBLIC_ variable into the bundle at build time, and Vite exposes every VITE_ variable the same way. A Supabase secret or service role key there bypasses RLS entirely; a Stripe secret key there has unrestricted permissions on Stripe's APIs.

Detect it by searching tracked files for secret key names and prefixes, then building the app and searching the output. Fix it by moving the key to a server-only variable without the public prefix, using it only in route handlers or server actions, and rotating it, because a key that reached a deployed bundle has been public.

git grep -n -e SERVICE_ROLE -e sb_secret_ -e sk_live_

# after next build
grep -rn -e sb_secret_ -e sk_live_ .next/static

Go deeper: Is the Supabase anon key safe to expose?

Go deeper: Supabase service role key leaked or exposed: how do I rotate it?

Unsigned webhooks

What it is: a Stripe webhook route that trusts whatever JSON it receives. Anyone who finds the URL can post a fake checkout.session.completed event and unlock a paid plan. Stripe's docs say to verify each event with the Stripe-Signature header and constructEvent over the raw request body; a body that was parsed and re-serialized fails verification.

Detect it by opening each webhook handler: one that calls req.json() before verifying, or never reads the stripe-signature header, is not verified. Fix it by verifying before anything else and returning 400 when verification fails.

const body = await req.text();
const signature = req.headers.get('stripe-signature') ?? '';
const event = stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);

Go deeper: Stripe webhook works locally but fails after deploy: how to diagnose and fix it

Security definer functions

What it is: a Postgres function created with security definer runs with its owner's rights, so RLS does not limit what it touches. Supabase's docs say any role can run a database function by default, and warn that a security definer function in an exposed schema "is callable over the Data API with the creator's privileges".

Detect it by listing the security definer functions anon can execute. Fix it by revoking execute from public, anon and authenticated when only your server calls the function, or by making it security invoker, the default Supabase prefers, so the caller's RLS applies. Either way, pin its search_path.

select p.oid::regprocedure as function
from pg_proc p
join pg_namespace n on n.oid = p.pronamespace
where n.nspname = 'public'
  and p.prosecdef
  and has_function_privilege('anon', p.oid, 'execute');

revoke execute on function public.delete_all_notes() from public, anon, authenticated;
alter function public.delete_all_notes() set search_path = '';

Go deeper: Can anyone call my Supabase RPC functions with the anon key?

.env in git

What it is: an env file with real keys that was committed, even once. Deleting it later leaves the keys in history, and GitHub notes that rewriting history does not reach a clone someone made before the rewrite.

Detect it by checking whether git tracks an env file now, and whether any commit ever touched one. Fix it by rotating every key that was in it first, which GitHub's guide puts before anything else, then adding the files to .gitignore and untracking them. GitHub says a rotated key may be enough on its own, since it no longer works.

git ls-files -- '.env*' '*/.env*'
git log --all --oneline -- .env .env.local

# after rotating the keys
git rm --cached .env

Missing rate limits

What it is: a route anyone can call as often as they like. On a vibe-coded app the expensive ones are AI calls, email sends and sign-ups. Supabase Auth enforces rate limits on its own endpoints, which you can tune in the dashboard; a route you write yourself, such as one that calls an AI provider, gets the limits you configure.

Detect it by listing your API routes and server actions and marking each one that calls a paid API or sends email. Fix it with a rate limiting rule in Vercel's WAF, which is available on all plans, or a limiter inside the route keyed on the user or IP address, plus a usage cap with the paid provider where it offers one.

Optional repo check

For AI-built Vercel + Supabase apps, npx -y viberaven@1.6.3 check is a pre-deploy review for vibe-coded Vercel + Supabase apps that looks for these six risks in your repo files. In runs on a fixture repo on 2026-10-05 it reported a public table without RLS and using (true) policies, NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY in .env.example, a Stripe webhook handler with no signature check, a security definer function the anon key could call, and a .env file not covered by .gitignore. It also flags API routes without rate limiting, though that check currently misses Windows paths: it fired on Linux and not on Windows for the same repo. It did not flag a Stripe secret key hardcoded in a client file, and it cannot see your git history or your live project. It is advice, not a gate, and a repository check, not a live database or security test. It exits with code 1 when it finds a blocker and writes a .viberaven folder that it does not add to .gitignore.

Related guides

Sources

Is vibe coding a security risk?

It can be, in specific and checkable ways. None of the risks above is unique to AI-written code, but an agent asked for a working feature may not add RLS policies, signature checks or rate limits unless you ask, and a builder who did not write the code may not know they are missing.

Which risk should I fix first?

Open RLS and secret keys in the client, because each one can expose every user's data at once. Unsigned webhooks come next if you take payments.