DEV Community

Tomer goldstein
Tomer goldstein

Posted on • Originally published at ship-safe.co

Is Lovable Actually Secure? I Checked the Supabase RLS on 50 Apps

So I've been on this thing where I scan AI-generated apps for security holes. Did it with Cursor first - 67% had critical vulns. That post blew up a bit so I figured — let's do Lovable next.

If you don't know Lovable: you describe what you want, it builds you a full React + Supabase app. Auth, database, the whole thing. It's honestly impressive for prototyping.

For production though? Yeah... about that.

I scanned 50 Lovable repos. Real apps that people actually deployed. Not toy projects.

The same 5 vulnerabilities showed up in almost every single one.

1. No Row Level Security — 89% of apps

This one's rough.

Lovable creates your Supabase tables but just... doesn't turn on RLS. No policies. Nothing.

What that means: any user who's logged into your app can read, edit, or delete every other user's data. They don't even need to hack anything — just call the Supabase API directly and skip your frontend.

-- What Lovable generates
CREATE TABLE public.projects (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  user_id UUID REFERENCES auth.users(id),
  name TEXT NOT NULL,
  data JSONB
);
-- That's it. No RLS. Your database is basically public.
Enter fullscreen mode Exit fullscreen mode

The fix takes 2 minutes per table:

ALTER TABLE public.projects ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users see own projects"
  ON public.projects FOR SELECT
  USING (auth.uid() = user_id);

CREATE POLICY "Users insert own projects"
  ON public.projects FOR INSERT
  WITH CHECK (auth.uid() = user_id);
Enter fullscreen mode Exit fullscreen mode

89% of apps didn't have this. Eighty-nine percent.

2. Service Role Key in Client Code — 34%

Lovable sometimes initializes the Supabase client with the service role key instead of the anon key. Quick explainer if you're not deep in Supabase: the service role key bypasses ALL security policies. It's the god-mode key.

When it's in your client-side code, it ships in your JavaScript bundle. Anyone who opens DevTools has it.

// nah fam
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY! // bypasses ALL RLS
);
Enter fullscreen mode Exit fullscreen mode

Fix: use the anon key on the client. Service role key stays server-side only. Always.

3. Querying auth.users Directly — 28%

Instead of creating a profiles table, Lovable sometimes queries auth.users directly. That table has hashed passwords and email confirmation tokens in it.

Just... create a profiles table. Set up a trigger to sync it. This is Supabase 101.

4. Secrets in VITE_ / NEXT_PUBLIC_ Vars — 22%

Lovable puts sensitive stuff behind VITE_ or NEXT_PUBLIC_ prefixes, which means they get bundled into the browser JavaScript.

Database connection strings. Webhook secrets. In the browser. Where anyone can read them.

Fix: if it's sensitive, don't prefix it. Access it from server-side code only.

5. Zero Input Validation — 18%

Form data goes straight to Supabase. No Zod, no type checking, nothing. Whatever someone sends, your database accepts.

The Pattern

Lovable optimizes for "it works." And it does work — fast. But "works" and "secure" are two very different things. The AI doesn't think about what happens when someone goes around your UI and talks to your database directly.

Every one of these fixes takes less than 30 minutes if you know what to look for.

Check Yours

If you shipped something with Lovable:

  1. Supabase dashboard → Table Editor → check if RLS is on (it's probably not)
  2. Search your code for SERVICE_ROLE — if it's in any client file, move it
  3. Search for VITE_ and NEXT_PUBLIC_ — are any of those actually secrets?

Or just paste your GitHub URL into ship-safe.co — free scan, 2 minutes, checks all of this.


Building ShipSafe — scans AI-generated apps for the stuff the AI forgot. Free, no card. If you vibe-code, you probably need this.

Top comments (0)