The Supabase AI plugin ships your schema fast. It also ships RLS bugs, leaked service role keys, and broken auth boundaries. Here is what to check.

You vibe-coded a SaaS with Cursor and the new Supabase AI plugin over the weekend. The schema came out clean. Auth works. You shipped to Vercel on Sunday night. By Wednesday, someone in your Discord posts a screenshot of another customer's billing page, which they got by changing one digit in the URL. The plugin wrote your tables, your queries, even your row-level policies. What it did not do was check whether the user_id on the request matched the user_id on the row. It assumed your frontend would handle that. Welcome to the new shape of the same old bug. AI plugins like Supabase's are great at moving you from idea to running code in an hour. They are bad at telling you which doors they left open. Here is what to check before your weekend project becomes a Twitter thread.

02The plugin writes code. It does not write your threat model.

Supabase's AI plugin will happily generate a new table, an insert query, and a working UI in the same prompt. What it will not reliably do is enable row-level security on that table. RLS is off by default on every new public schema table in Supabase. If you do not turn it on, every authenticated user can read every row. The plugin sometimes adds a CREATE POLICY statement. It also sometimes forgets. And when it forgets, your app still works, because the frontend filters by user_id in the query. The bug only shows up when someone opens the network tab and changes the user_id parameter, or hits your /rest/v1/orders endpoint directly with their own JWT. Open the Supabase SQL editor and run this right now: SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'. Any table where rowsecurity is false is a public table. Then for the ones that say true, run SELECT * FROM pg_policies WHERE schemaname = 'public' and read every policy. If a SELECT policy on your orders table does not include auth.uid() = user_id, you do not have access control, you have the illusion of it.

03Service role keys end up in places they should not be

The Supabase service role key bypasses RLS entirely. It is the master key. When an AI plugin needs to do something the anon key cannot do, like insert a row from a webhook, it reaches for the service role key. That is fine in a server-side function. It is a disaster if the same code runs on the client. Cursor and similar tools do not always know which side of the wire they are writing for. They will import the service role key into a file that gets bundled into your Next.js client, or use it in a /api route with no auth check on the caller, turning that route into a public bypass of every policy you wrote. Three checks for the next ten minutes. One: grep your repo for SUPABASE_SERVICE_ROLE_KEY and confirm every use is in a server-only file, never under /components or imported by a 'use client' file. Two: open your Vercel project, check the service role key is not prefixed with NEXT_PUBLIC_, because that prefix ships it to the browser. Three: rotate the key in the Supabase dashboard if you cannot account for every place it has been pasted, including chat logs with the AI assistant.

04Your API routes are not as private as they look

The other failure mode is the /api/admin or /api/orders route that checks the user is signed in but never checks what they are allowed to do. The AI plugin generates a route that reads the session, pulls the user, and runs a query. It looks correct. It is not. If the route takes an organization_id from the request body and trusts it, any signed-in user can pass another organization's ID and get back data that is not theirs. This is the modal vibe-coded bug. Try it on your own app. Sign in as one user. Open your network tab, find a request with an ID you control, like user_id, organization_id, or order_id. Change one digit. Resend. If the response comes back with someone else's data, you have the bug. Same drill for your Stripe webhook handler, which should verify the signature before doing anything. Same for any public bucket in Supabase Storage you flipped to public for testing and never flipped back. Guardian scans your Supabase project, your repo, and your deployed app together, and tells you which tables have RLS off, which API routes leak across user boundaries, and which keys ended up in the wrong file. If you shipped something with the Supabase AI plugin this month, run Guardian on it before someone else does.

The Guardian Team
Security for apps built with AI.

Find the RLS holes the Supabase AI plugin left in your app

Guardian scans your Supabase schema, env vars, and API routes together to flag tables with RLS off, exposed service role keys, and routes that trust user-supplied IDs.

Scan my app free
More articles