The question lands when a Lovable.dev builder ships an app, opens DevTools out of curiosity, and sees a long JWT string sitting in plain view inside a bundled JavaScript file. The string begins with eyJ and contains the word service_role. The app is live. The Supabase project behind it holds real user records. The next question is whether anything can still be done.
Short answer
A Lovable.dev app is exposing the Supabase service_role key whenever that key is reachable from a browser. The service_role key runs as the postgres role inside Supabase and bypasses every Row Level Security policy, which Supabase's own API keys documentation and Row Level Security guide describe in those exact terms. Anyone who reads the key from the bundle can dump the entire database, delete tables, and write fake records. The fix is to rotate the key in the Supabase dashboard, remove every copy from client code and the Lovable chat, switch the client to the anon key, and enable RLS on every public table.
What you should know
- The service_role key is a superuser inside the database role hierarchy. Supabase's API keys reference describes it as a secret that bypasses RLS and warns it should only run on servers you control.
- Vite inlines any variable prefixed with VITE_ into the browser bundle. A Lovable.dev project that stores the service_role key as VITE_SUPABASE_SERVICE_ROLE ships it to every visitor.
- The Lovable.dev chat history is a parallel leak surface. Keys pasted into a debugging chat sit inside the project artefacts and have been observed in cross-tenant exposures reported on HackerOne in 2026.
- A research scan by SupaExplorer found that 11% of 20,000 indie launch URLs exposed Supabase credentials in their frontend code, and some of those carried the service_role key directly.
- The fix is rotation first, code change second. Removing the key from the bundle on its own does not invalidate the copies attackers may already hold.
Why does the service_role key end up in a Lovable.dev bundle in the first place?
The official Lovable.dev Supabase integration documented in Lovable's Supabase guide uses OAuth and places third-party secrets inside Supabase Edge Function secrets. That path is safe. The unsafe path opens when a Lovable.dev builder runs into a Row Level Security error during a Lovable chat session, asks the model to make the query work, and pastes the service_role key from the Supabase API page into the conversation. The model writes a supabase-js client that reads the key from an environment variable, the builder adds it to .env as VITE_SUPABASE_SERVICE_ROLE, and Vite inlines that string into the JavaScript bundle Lovable serves on the public URL.
A second common path is direct hardcoding. A frustrated builder pastes the key into src/supabaseClient.ts to skip the env step. The file commits to GitHub, deploys, and the key now lives in three places: the public bundle, the repository history, and the Lovable chat. Each location must be cleaned independently.
A third path opens when the same Lovable.dev project links a Supabase Edge Function that requires service_role privileges, and the builder copies the function code into a frontend file by mistake while iterating on a feature. The Lovable preview happily renders the page; the audit happens later, in production.
None of these paths involve a deliberate decision to ship a secret. They all involve a debugging shortcut that the build pipeline silently turns into a public broadcast.
What can an attacker actually do with a leaked Supabase service_role key?
The Supabase RLS documentation states that the service_role key bypasses every policy on every table in the public schema. That sentence translates into a concrete attack surface:
- Read every row in every table, including columns scoped to a single user by an RLS policy.
- Insert, update, and delete any row, including records that belong to other users.
- Call any database function exposed through PostgREST, including functions intended for internal use.
- Run schema-altering statements through the REST endpoint where the role grants allow it.
- Read storage buckets that depend on RLS for access control.
The Superblocks write-up of CVE-2025-48757 describes a related class of exposure on Lovable-generated apps where 170 of 1,645 scanned projects, roughly 10.3%, presented RLS failures that allowed remote unauthenticated dumps. That class of exposure used the anon key with broken policies. The service_role exposure removes the policy step entirely. A single curl request with the leaked key against the project REST endpoint pulls every row.
How does the service_role key differ from the anon key in practice?
The two keys map to two different Postgres roles. The anon key maps to the anon role, which has only the table grants RLS allows. The service_role key maps to a role that bypasses RLS and inherits the full table grants Supabase issues by default. The table below summarises the practical effect on a Lovable.dev project.
| Property | anon key | service_role key |
|---|---|---|
| Postgres role | anon | service_role |
| Respects RLS | Yes, every query is policy-checked | No, every query is unrestricted |
| Safe to ship in the browser | Yes, when RLS is correct on every public table | No, never |
| Where Supabase recommends it | Public client code, mobile app builds | Edge Functions, trusted servers, CI jobs |
| Effect of a leak | Bounded by RLS quality | Full database read and write |
| Newer secret-key format | sb_publishable_ | sb_secret_, blocked from browser User-Agent |
The Supabase API keys documentation describes one additional defence: the modern sb_secret_ format returns HTTP 401 when the request carries a browser User-Agent header. That defence does not save a project whose key is the legacy JWT format. It also does not save a project where an attacker swaps the User-Agent for curl or any HTTP client.
How do I detect a service_role exposure in a Lovable.dev project right now?
Four checks cover the common leak paths. Run them in order.
- Inspect the deployed bundle. Open the live Lovable.dev URL in Chrome. Open DevTools, switch to Sources, and use Cmd-Option-F to search across loaded scripts for sb_secret_, service_role, and the first six characters of the eyJ JWT prefix. A hit in a .js file means the key reaches every visitor.
- Grep the GitHub repository. Run a search across the repo, including past commits, for the same three strings. A historical commit still carries the key even after a later commit removes it. GitHub's secret scanning surfaces some, but not all, Supabase keys.
- Search the Lovable chat history. Scroll the project chat for service_role and for full JWT strings. A key pasted into the chat sits inside Lovable project artefacts that, per the disclosure timeline reported on Lovable in March and April 2026, have at points been reachable across accounts.
- Check the env file for VITE_ prefixed secrets. Open .env and .env.production. Any variable that starts with VITE_ ships in the bundle. A SUPABASE_SERVICE_ROLE_KEY without the VITE_ prefix is safer, but only if no code reads it through import.meta.env.
A negative result on all four checks is the only state that lets the rest of the response be lighter than a full rotation.
What is the safest recovery path after a confirmed service_role leak?
The order matters. Rotate before you change code, because the old key remains valid until it is deleted in the Supabase dashboard, and any caller who scraped the bundle already holds a copy.
- Generate a new secret API key. Open the Supabase project, go to Settings, API Keys, create a new sb_secret_ key, copy it, store it in the Supabase Edge Function secret manager or in your hosting provider's secret store.
- Replace the old key everywhere it legitimately lives. Edge Functions, GitHub Actions, server workers, CI pipelines. Redeploy them.
- Delete the compromised key. The Supabase dashboard warns that deletion is permanent. Confirm only after every legitimate caller is on the new key.
- Move the Lovable.dev client to the anon key. Edit supabaseClient.ts and any related file so the createClient call reads VITE_SUPABASE_ANON_KEY only.
- Enable RLS on every public table. Visit each table in the Supabase Table Editor and turn on Row Level Security. Add the policies the application needs. The default of no policies blocks all access through the anon key, which is the right starting point.
- Audit the database for damage. Read audit logs for write activity from the service_role between the leak window and the rotation. Restore from backup any table the audit cannot vouch for.
For builders who want an external automated read of the deployed bundle, the repository, and the Supabase project before the next release, PTKD.com (https://ptkd.com) is one of the platforms that scans compiled or deployed AI-coded apps and flags exposed Supabase keys, missing RLS policies, and other vibe-coding security gaps before they reach production users. The output names the key prefix it found and the file path that carried it, which shortens the loop between a leak and a rotation.
What does Lovable.dev itself do to prevent this exposure?
Lovable's published security documentation places third-party secrets inside Supabase Edge Function secrets and recommends Row Level Security on every public table. The official integration flow uses OAuth and does not ask the builder to handle the service_role key. The exposure pattern observed in the wild is not the documented flow; it is the debugging shortcut the documentation does not catch.
The platform also carries a separate exposure history. Independent reports in early 2026 described a Broken Object Level Authorization issue in Lovable's project API that, per disclosure write-ups, made project artefacts reachable across free accounts for a window of 48 days. The reports recommended rotating any credential that ever sat inside a Lovable project, including Supabase keys, Stripe keys, and OpenAI tokens. Treat any pre-2026 Lovable project as having a non-zero chance that its chat history was readable by another account, and rotate accordingly.
What to watch out for
A few patterns turn a real fix into a partial one.
- Removing the key from the bundle without rotating it. The bundle hosted on a CDN is cached. Attackers already have copies.
- Rotating the key but leaving RLS off. A correctly configured anon key with no policies still hands every row to any visitor.
- Confusing the publishable and secret formats. sb_publishable_ is meant for the browser. sb_secret_ is not.
- Trusting GitHub secret scanning alone. Supabase keys in the form of JWTs are not consistently caught by every scanner.
- Pasting the new key into a Lovable chat after rotation. A second exposure across the same surface, days after the first.
Key takeaways
- The service_role key bypasses Row Level Security on every public table, which is why Supabase ships it as a server-side secret and warns against shipping it in client code.
- A Lovable.dev project most often leaks the key by pasting it into a chat, prefixing it with VITE_, or hardcoding it into a client file during a debugging session.
- Detection takes four checks: the deployed bundle, the GitHub repository, the Lovable chat history, and the env files.
- Recovery is rotation first, code change second, RLS audit third. The order is fixed because the old key stays valid until it is deleted.
- Some teams outsource the pre-release scan of their AI-coded apps to platforms like PTKD.com (https://ptkd.com), which flags exposed Supabase keys, missing RLS policies, and other vibe-coding security gaps before the project goes live.



