Example finding How it works Coverage PENTEST METHODOLOGY DOCS PRICING FAQ MCP CONTACT LOG IN SIGN UP →

Lovable app shows everyone's data: how to fix it

FIX / LOVABLE SHARED DATA / 8 MIN READ

If your Lovable app shows the data of all users, the table has no owner rule. Enable Row Level Security (RLS), add an owner policy, and test with two accounts. The page code is not the cause. The page only shows what the database returns.

This guide covers Lovable apps that use a Supabase database. It shows how to confirm the leak, how to fix it, and how to check the damage.

Why does my Lovable app show everyone’s data?

Lovable builds the page and the database together. The page runs in the browser and reads the database through the Supabase API. The browser holds a public key for this purpose. The Lovable security documentation states that RLS policies control which users can access or modify data in your database. It also states that misconfigured RLS rules are a common cause of data leaks.

The database applies a rule only if the rule exists. If a table has no RLS, any request with the public key can read the table. If a table has a policy that allows everyone, the result is the same. The page code can add a filter, but the filter does not stop anyone who sends a request without your page.

Here is a typical case. You build a notes app. You create two test accounts. Each account must see only its own notes. The first account works, because you created all notes with it. You then sign in with a second account and see the same notes.

You can see the cause in the page code. The code asks for all rows of the table. A request like select("*") has no owner filter. The database returns every row that the policies allow. If no policy limits the rows, the result is every row.

How do I confirm that the leak exists?

Use two test accounts. Do not use the account that owns the project.

  1. Sign in to your published app as account A. Create one record.
  2. Sign out. Sign in as account B in a different browser profile.
  3. Open the page that lists records. If you see the record of account A, the leak exists.

Now check if the leak also works without your page. Anyone can find the Supabase URL and the public key in your app, because the browser sends them with each request. Open DevTools and select the Network tab. Reload the page. Find a request to supabase.co that reads your table.

curl -s "https://YOUR-PROJECT.supabase.co/rest/v1/notes?select=*&limit=5" \
  -H "apikey: YOUR_PUBLIC_KEY"

Replace notes with the name of your table. Send the request without a user token. If you receive rows, then visitors who do not have an account can read the table. This case is the most serious one.

Warning: Test only on your own app. Use only your own test records. Do not read the rows of real users to confirm the leak.

Also open the table list in the Lovable project. Lovable documents the path More, Database, Manage, Policies for policy review. Look at each table that holds user data. A table with no policy, or a policy that says true, is open.

How do I stop the leak now?

Work in this order. The first two steps stop the leak. The other steps remove the cause.

Caution: After you enable RLS, a table with no policy returns no rows. Your app can show empty pages for a short time. Add the policies in step 2 right away.

  1. Enable RLS on each table that holds user data. Use the SQL Editor of the Supabase project. The command is in the next section.
  2. Add an owner policy to each table. The policy compares the owner column of the row to the signed-in user.
  3. Delete policies that allow everyone. Look for true in the policy rule. Keep such a policy only on data that is public on purpose.
  4. Check the views. Set security_invoker = true on any view that reads a private table.
  5. Check the functions that Lovable created. A function with security definer can return rows without an owner check.
  6. Publish again and test with the two accounts. Repeat the checks from the confirm section.

If you find a service_role key in your app code or in a public variable, rotate it first. See what to do about a leaked service_role key. That key bypasses all RLS rules.

What SQL do I run to fix a table?

Each table needs an owner column. Most Lovable tables use user_id. Check your table first. If the column does not exist, add it. Existing rows have no owner, so you must assign them to real users before the policy can work.

alter table public.notes enable row level security;

alter table public.notes
  add column if not exists user_id uuid references auth.users (id)
  default auth.uid();

Now add the policies. The syntax follows the Supabase RLS documentation:

create policy "owner can read" on public.notes
  for select to authenticated
  using ( (select auth.uid()) = user_id );

create policy "owner can insert" on public.notes
  for insert to authenticated
  with check ( (select auth.uid()) = user_id );

create policy "owner can update" on public.notes
  for update to authenticated
  using ( (select auth.uid()) = user_id )
  with check ( (select auth.uid()) = user_id );

create policy "owner can delete" on public.notes
  for delete to authenticated
  using ( (select auth.uid()) = user_id );

The using clause limits the rows that the user can see or change. The with check clause limits the new version of the row. Without with check on updates, a user can change user_id and give a row to another account. The Supabase documentation also states that an update needs a matching select policy.

The policies use the authenticated role. A visitor who is not signed in receives no rows. If part of your app is public, such as a list of products, add a separate read policy for that table only.

For team features, shared workspaces, and role rules, read the Supabase RLS guide. This article covers the owner rule only.

What if only one page shows the wrong data?

A leak on one page can have a different cause from a leak on all pages. Check these cases.

  1. The page uses a view. A view over a private table can return all rows, even if the table has RLS. Set security_invoker = true on the view. The Supabase documentation states that views bypass RLS by default.
  2. The page calls a function. A database function can return rows for any user if it does not check the owner. Add the owner check inside the function, or change it to run with the rights of the caller.
  3. The page calls an Edge Function. A function that uses the service_role key bypasses RLS. Make the function read the user from the token. Then filter every query by that user.
  4. The page shows data from a shared table. Some tables hold data for all users on purpose, such as a list of plans. Check that no private column sits in such a table.

To find the source, open the Network tab and find the request that loads the page data. The URL shows the table, the view, or the function. Then look at the policies for that exact name.

A second sign is a count that is too high. If a dashboard shows a total for all users, RLS is off or the policy is too broad. A counting query reads every row that the policies allow.

Can I ask Lovable to fix it?

Yes, but you must check the result. A prompt is faster than writing SQL, but a prompt does not prove that the fix works.

Use a precise prompt. Name the tables. Describe the rule in one sentence per table. For example:

Enable RLS on the tables notes and tasks. Add select, insert, update and
delete policies so that a user can only access rows where user_id equals
auth.uid(). Add a user_id column with default auth.uid() if it is missing.
Do not change any other table. Show me the SQL you run.

After Lovable finishes, do these checks:

  1. Open More, Database, Manage, Policies and read each policy.
  2. Look for the owner comparison in both using and with check.
  3. Look for true in any policy on a private table.
  4. Run the two-account test on the published app.

If the policies are not what you asked for, run the SQL from the previous section yourself. A prompt can also change other code. Check that the app still works after the change.

Does the Lovable security scan find this?

It can find part of it. The Lovable documentation describes a Quick scan that runs automatically each time you publish. The documentation says the database review of this scan flags tables that lack per-record access control and rules that allow everyone through. You can also run a Deep scan, which reviews your application code for problems in logic, permissions, and data.

The same documentation warns that the scans cannot guarantee complete security. Treat a clean scan as one input, not as proof. The two-account test is the proof, because it shows what a real user receives.

For more checks on Lovable apps, read is my Lovable app secure.

How do I check if someone already read the data?

You cannot prove that nobody read it. You can look for signs in the Supabase logs.

  1. Open the logs. In the Supabase dashboard, open Logs. The Supabase logs documentation states that the explorer queries Postgres and API Gateway logs by default. The retention time depends on your plan.
  2. Look at requests to the leaking table. Look for one client that sends many requests. Look for requests that carry only the public key and no user token.
  3. Look at the time range. Start at the date when you created the table or removed its policies. Look at the whole period since then.
  4. Check the new accounts. Look for accounts that you do not know. Look for sign-ups that happened just before heavy reads.
  5. Check the other tables. Repeat the check for every table that had the same problem.

The logs show that requests arrived. They do not always show which rows each request returned. If the logs are short, assume that the data was read.

If the rows held personal data, write down which tables were open and for how long. You may have a legal duty to tell your users. Ask a lawyer who knows the rules in your region.

How do I prevent this in the next Lovable project?

  1. Ask for RLS in the first prompt. State that every table needs RLS and an owner policy.
  2. Read the policies after each new table. Open More, Database, Manage, Policies and look at the new table. This takes one minute.
  3. Never accept a policy with true on private data. Reject it and ask for the owner rule.
  4. Keep the secret keys out of the page code. Only the public key belongs in the browser. See the guide to the Supabase anon key to learn what this key allows.
  5. Run the two-account test before each launch. Create a record with account A. Try to read, change, and delete it with account B. All three actions must fail.
  6. Test the published app. Use the real URL and real test accounts, not only the editor.
  7. Scan after each major change. A new feature can add a new table without a policy.

For the full Lovable background, see the Lovable guide.

When should I get help?

Get help in these cases:

  • The open table held payment data, health data, or identity documents. Ask a security engineer and a lawyer now.
  • The logs show an unknown client that sent many requests to the table.
  • The app has many tables, and you cannot say which ones are open.
  • You use the service_role key in the page code and do not know what else it opened.
  • You fixed the policies, but the two-account test still shows the data of the other account.

After the fix, run the two-account test on the published URL one more time. Check the list page, a detail page, and an edit action. Each one must refuse the data of the other account.

Common questions

Why does my Lovable app show the data of other users?
The database table has no rule that limits each user to their own rows. The app code asks for rows, and the database returns all of them.
Can I fix this with a prompt to Lovable only?
A prompt can add the policy, but you must check the result. Read the policy in the dashboard and test with two accounts.
Does the Lovable security scan find this problem?
The Lovable docs say the Quick scan flags tables that lack per-record access control. The docs also say the scan cannot guarantee complete security.
Is a filter in the page code enough?
No. A filter in the page code does not stop a user who calls the API directly. The rule must exist in the database.
Do I need to delete the app?
No. Add the missing policies and test them. Delete data only if the data is wrong or if a law requires it.

Check your lovable tables

A VibeEval scan calls the API of your deployed Lovable app and checks if a table returns the rows of other users.

14-day free trial · No credit card · Cancel anytime

SCAN YOUR APP →