Sample report with demo data. The app, URL, Supabase project and keys are placeholders. Every key is fake and masked the same way your real report masks them. Generated by running our real scanner against a mock app, so every finding type here is one we really produce.
Full launch report
https://your-app.lovable.app/
Scanned Oct 18, 2026 · 17 findings · passive read-only
F
Grade
3
critical
1
high
4
medium
6
low
3
info
Biggest risks
Leaked keys and data anyone can read. Fix these before you launch.
CRITICAL
Stripe secret key exposed in JS:index-Dk3x9QpA.js
Found in your shipped code (JS:index-Dk3x9QpA.js): sk_liv…5625. Anyone can copy it. Rotate it and move it server-side.
CRITICAL
Supabase table "profiles" readable without login (columns include email, phone, stripe_customer_id)
Anyone with your public anon key can download rows from this table. Sensitive-looking columns: email, phone, stripe_customer_id. Fix: Row Level Security policies.
CRITICAL
Supabase table "payments" readable without login (columns include stripe_customer_id)
Anyone with your public anon key can download rows from this table. Sensitive-looking columns: stripe_customer_id. Fix: Row Level Security policies.
HIGH
Supabase Storage bucket "invoices" lets anyone list its files
Anyone can list files in your Supabase Storage with the public key. Fix: storage.objects policies.
MEDIUM
Supabase table "posts" is publicly readable (fine only if meant to be public)
Anyone with your public anon key can download rows from this table. Fix: Row Level Security policies.
The full report has the evidence and copy-paste fixes (exact RLS SQL / Firebase rules + prompts for Cursor, Claude Code, Lovable, Bolt and v0).
What we checked
Scanned your HTML and 1 same-origin JS file for leaked keys (Stripe, OpenAI, Anthropic, AWS, GitHub, Slack, Google, Supabase secret keys).
Supabase: tested 6 tables (of 6 listed) and 1 storage bucket with your public anon key.
Libraries: detected jQuery 3.4.1 and compared them with our list of known vulnerable versions.
CORS: sent one request with a foreign Origin header to see if your server echoes it back with credentials allowed.
Email DNS: skipped (platform subdomain such as *.vercel.app, where you cannot set SPF/DMARC). Also checked for /.well-known/security.txt.
After fixing: the free re-check
The owner turned on RLS for profiles and payments, rotated the Stripe key and locked the invoices bucket, then pressed "Re-check my site". This is what the re-check shows:
Before / after
Grade F → D (score 0 → 47; first scan Oct 18, 2026)
Fixed 4
CRITICALStripe secret key exposed in JS:index-Dk3x9QpA.js
CRITICALSupabase table "profiles" readable without login (columns include email, phone, stripe_customer_id)
CRITICALSupabase table "payments" readable without login (columns include stripe_customer_id)
HIGHSupabase Storage bucket "invoices" lets anyone list its files
Still open 13
MEDIUMMissing Content-Security-Policy
MEDIUMjQuery 3.4.1 has known vulnerabilities (CVE-2020-11022)
MEDIUMSource map publicly downloadable
MEDIUMSupabase table "posts" is publicly readable (fine only if meant to be public)
LOWMissing X-Frame-Options / frame-ancestors
LOWMissing X-Content-Type-Options
LOWMissing meta description
LOWCORS reflects any origin (without credentials)
LOWSupabase schema lists 6 tables to anonymous users
LOWSupabase Storage bucket list visible to anonymous users (1)
INFOSupabase project URL in client
INFOSupabase anon key in client (expected)
+1 more
New 0
None
Fix prompts for:
CRITICAL
Stripe secret key exposed in JS:index-Dk3x9QpA.js
Pattern matching Stripe secret key found in JS:index-Dk3x9QpA.js. Redacted: sk_liv…5625.
Evidence: sk_liv…5625
Plain-English fix
A Stripe secret key (sk_ / rk_) is in your public JavaScript, so anyone can copy it and use it on your bill or your data. Rotate it in Stripe Dashboard > Developers > API keys (roll the key), move every call that needs it to server-side code, and keep the key in a private environment variable. Only the publishable key (pk_) belongs in the browser.
Prompt for Cursor
Security fix: a Stripe secret key (sk_ / rk_) is exposed in the frontend bundle.
1) Find every place the key is read or hard-coded (search for the key prefix, STRIPE_SECRET_KEY, and any VITE_/NEXT_PUBLIC_/REACT_APP_/EXPO_PUBLIC_ variant of it).
2) Move every call that uses it into server-only code (API route, server action, or Supabase Edge Function) that reads STRIPE_SECRET_KEY from a private env var. Never prefix it with VITE_ or NEXT_PUBLIC_.
3) Change the frontend to call that server endpoint instead, and require a logged-in user plus basic rate limiting on it.
4) Remove the old public env var from .env files and from the hosting dashboard.
IMPORTANT: removing the key from code is not enough. The old key is already public, so I will rotate it myself in Stripe Dashboard > Developers > API keys (roll the key); remind me at the end.
How to check it worked: Rebuild, open the live site, and search the JS files in DevTools > Sources for the key prefix. It should not appear.
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Security fix: a Stripe secret key (sk_ / rk_) is exposed in the frontend bundle.
1) Find every place the key is read or hard-coded (search for the key prefix, STRIPE_SECRET_KEY, and any VITE_/NEXT_PUBLIC_/REACT_APP_/EXPO_PUBLIC_ variant of it).
2) Move every call that uses it into server-only code (API route, server action, or Supabase Edge Function) that reads STRIPE_SECRET_KEY from a private env var. Never prefix it with VITE_ or NEXT_PUBLIC_.
3) Change the frontend to call that server endpoint instead, and require a logged-in user plus basic rate limiting on it.
4) Remove the old public env var from .env files and from the hosting dashboard.
IMPORTANT: removing the key from code is not enough. The old key is already public, so I will rotate it myself in Stripe Dashboard > Developers > API keys (roll the key); remind me at the end.
How to check it worked: Rebuild, open the live site, and search the JS files in DevTools > Sources for the key prefix. It should not appear.
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Security fix: my Stripe secret key (sk_ / rk_) is visible in the browser code. Move everything that uses it into a secure backend/edge function, store it as a private secret named STRIPE_SECRET_KEY (not a public/VITE_/NEXT_PUBLIC_ variable), make the frontend call that function instead, and remove the key from all client code. IMPORTANT: removing the key from code is not enough. The old key is already public, so I will rotate it myself in Stripe Dashboard > Developers > API keys (roll the key); remind me at the end.
How to check it worked: Rebuild, open the live site, and search the JS files in DevTools > Sources for the key prefix. It should not appear.
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Security fix: my Stripe secret key (sk_ / rk_) is visible in the browser code. Move everything that uses it into a secure backend/edge function, store it as a private secret named STRIPE_SECRET_KEY (not a public/VITE_/NEXT_PUBLIC_ variable), make the frontend call that function instead, and remove the key from all client code. IMPORTANT: removing the key from code is not enough. The old key is already public, so I will rotate it myself in Stripe Dashboard > Developers > API keys (roll the key); remind me at the end.
How to check it worked: Rebuild, open the live site, and search the JS files in DevTools > Sources for the key prefix. It should not appear.
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Security fix: my Stripe secret key (sk_ / rk_) is visible in the browser code. Move everything that uses it into a secure backend/edge function, store it as a private secret named STRIPE_SECRET_KEY (not a public/VITE_/NEXT_PUBLIC_ variable), make the frontend call that function instead, and remove the key from all client code. IMPORTANT: removing the key from code is not enough. The old key is already public, so I will rotate it myself in Stripe Dashboard > Developers > API keys (roll the key); remind me at the end.
How to check it worked: Rebuild, open the live site, and search the JS files in DevTools > Sources for the key prefix. It should not appear.
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
CRITICAL
Supabase table "profiles" readable without login (columns include email, phone, stripe_customer_id)
A read-only GET to https://abcdefghijklmnopqrst.supabase.co/rest/v1/profiles?limit=1 with only the public anon key (which ships in your JavaScript) returned a row, so anyone can download this table. Row Level Security is off or has a policy that allows anonymous reads. Sensitive-looking columns: email, phone, stripe_customer_id. (We did not store or display any row values.)
Plain-English fix
Anyone can download rows from "profiles" using the public anon key that ships in your JavaScript. Turn on Row Level Security for it and add policies so that each signed-in user can only read and edit their own rows; logged-out visitors get nothing.
Supabase SQL editor (paste and run)
-- Fix anonymous access to public.profiles
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.profiles enable row level security;
-- 2) First delete any "allow all" / "true" policies on profiles: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'profiles';)
-- 3) Logged-out visitors get nothing
revoke all on table public.profiles from anon;
-- 4) Each signed-in user can read and edit only their own row(s)
create policy "profiles_select_own" on public.profiles for select to authenticated using ((select auth.uid()) = id);
create policy "profiles_insert_own" on public.profiles for insert to authenticated with check ((select auth.uid()) = id);
create policy "profiles_update_own" on public.profiles for update to authenticated using ((select auth.uid()) = id) with check ((select auth.uid()) = id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
-- Then check from a terminal:
-- curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/profiles?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Prompt for Cursor
CRITICAL: the Supabase table public.profiles is readable with only the anon key. It has sensitive columns: email, phone, stripe_customer_id.
Goal: each signed-in user can only read and edit their own rows; logged-out visitors get nothing.
1) Create a migration (supabase/migrations/<timestamp>_rls_profiles.sql) with exactly this SQL and apply it (supabase db push, or run it in the SQL editor):
-- Fix anonymous access to public.profiles
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.profiles enable row level security;
-- 2) First delete any "allow all" / "true" policies on profiles: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'profiles';)
-- 3) Logged-out visitors get nothing
revoke all on table public.profiles from anon;
-- 4) Each signed-in user can read and edit only their own row(s)
create policy "profiles_select_own" on public.profiles for select to authenticated using ((select auth.uid()) = id);
create policy "profiles_insert_own" on public.profiles for insert to authenticated with check ((select auth.uid()) = id);
create policy "profiles_update_own" on public.profiles for update to authenticated using ((select auth.uid()) = id) with check ((select auth.uid()) = id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
2) Find every supabase.from('profiles') call and make sure it runs with a logged-in session; move anything that must read other users' rows into a server route / Edge Function using the service role key.
3) Check every other table the app uses for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/profiles?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
CRITICAL: the Supabase table public.profiles is readable with only the anon key. It has sensitive columns: email, phone, stripe_customer_id.
Goal: each signed-in user can only read and edit their own rows; logged-out visitors get nothing.
1) Create a migration (supabase/migrations/<timestamp>_rls_profiles.sql) with exactly this SQL and apply it (supabase db push, or run it in the SQL editor):
-- Fix anonymous access to public.profiles
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.profiles enable row level security;
-- 2) First delete any "allow all" / "true" policies on profiles: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'profiles';)
-- 3) Logged-out visitors get nothing
revoke all on table public.profiles from anon;
-- 4) Each signed-in user can read and edit only their own row(s)
create policy "profiles_select_own" on public.profiles for select to authenticated using ((select auth.uid()) = id);
create policy "profiles_insert_own" on public.profiles for insert to authenticated with check ((select auth.uid()) = id);
create policy "profiles_update_own" on public.profiles for update to authenticated using ((select auth.uid()) = id) with check ((select auth.uid()) = id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
2) Find every supabase.from('profiles') call and make sure it runs with a logged-in session; move anything that must read other users' rows into a server route / Edge Function using the service role key.
3) Check every other table the app uses for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/profiles?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Security fix: anyone can read my Supabase table "profiles" without logging in. Turn on Row Level Security for it so that each signed-in user can only read and edit their own rows; logged-out visitors get nothing. Use this SQL as the migration (adjust the owner column if it is named differently):
-- Fix anonymous access to public.profiles
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.profiles enable row level security;
-- 2) First delete any "allow all" / "true" policies on profiles: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'profiles';)
-- 3) Logged-out visitors get nothing
revoke all on table public.profiles from anon;
-- 4) Each signed-in user can read and edit only their own row(s)
create policy "profiles_select_own" on public.profiles for select to authenticated using ((select auth.uid()) = id);
create policy "profiles_insert_own" on public.profiles for insert to authenticated with check ((select auth.uid()) = id);
create policy "profiles_update_own" on public.profiles for update to authenticated using ((select auth.uid()) = id) with check ((select auth.uid()) = id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
Then make sure the app still works for logged-in users, and check my other tables for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/profiles?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Apply this to my project (run the SQL as a Supabase migration on the connected project), keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Security fix: anyone can read my Supabase table "profiles" without logging in. Turn on Row Level Security for it so that each signed-in user can only read and edit their own rows; logged-out visitors get nothing. Use this SQL as the migration (adjust the owner column if it is named differently):
-- Fix anonymous access to public.profiles
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.profiles enable row level security;
-- 2) First delete any "allow all" / "true" policies on profiles: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'profiles';)
-- 3) Logged-out visitors get nothing
revoke all on table public.profiles from anon;
-- 4) Each signed-in user can read and edit only their own row(s)
create policy "profiles_select_own" on public.profiles for select to authenticated using ((select auth.uid()) = id);
create policy "profiles_insert_own" on public.profiles for insert to authenticated with check ((select auth.uid()) = id);
create policy "profiles_update_own" on public.profiles for update to authenticated using ((select auth.uid()) = id) with check ((select auth.uid()) = id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
Then make sure the app still works for logged-in users, and check my other tables for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/profiles?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Make the change in this project (put the SQL in a new file under supabase/migrations and apply it to the connected Supabase project), keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Security fix: anyone can read my Supabase table "profiles" without logging in. Turn on Row Level Security for it so that each signed-in user can only read and edit their own rows; logged-out visitors get nothing. Use this SQL as the migration (adjust the owner column if it is named differently):
-- Fix anonymous access to public.profiles
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.profiles enable row level security;
-- 2) First delete any "allow all" / "true" policies on profiles: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'profiles';)
-- 3) Logged-out visitors get nothing
revoke all on table public.profiles from anon;
-- 4) Each signed-in user can read and edit only their own row(s)
create policy "profiles_select_own" on public.profiles for select to authenticated using ((select auth.uid()) = id);
create policy "profiles_insert_own" on public.profiles for insert to authenticated with check ((select auth.uid()) = id);
create policy "profiles_update_own" on public.profiles for update to authenticated using ((select auth.uid()) = id) with check ((select auth.uid()) = id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
Then make sure the app still works for logged-in users, and check my other tables for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/profiles?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
CRITICAL
Supabase table "payments" readable without login (columns include stripe_customer_id)
A read-only GET to https://abcdefghijklmnopqrst.supabase.co/rest/v1/payments?limit=1 with only the public anon key (which ships in your JavaScript) returned a row, so anyone can download this table. Row Level Security is off or has a policy that allows anonymous reads. Sensitive-looking columns: stripe_customer_id. (We did not store or display any row values.)
Plain-English fix
Anyone can download rows from "payments" using the public anon key that ships in your JavaScript. Turn on Row Level Security for it and add policies so that logged-out visitors get nothing, signed-in users can only read their own rows, and rows are only written by the server/webhook with the service role key.
Supabase SQL editor (paste and run)
-- Fix anonymous access to public.payments
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.payments enable row level security;
-- 2) First delete any "allow all" / "true" policies on payments: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'payments';)
-- 3) Logged-out visitors get nothing
revoke all on table public.payments from anon;
-- 4) Signed-in users can read only their own rows
create policy "payments_select_own" on public.payments for select to authenticated using ((select auth.uid()) = user_id);
-- 5) No insert/update/delete policies on purpose: write these rows only from your server
-- or Stripe webhook using the service role key (never from the browser).
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
-- Then check from a terminal:
-- curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/payments?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Prompt for Cursor
CRITICAL: the Supabase table public.payments is readable with only the anon key. It has sensitive columns: stripe_customer_id.
Goal: logged-out visitors get nothing, signed-in users can only read their own rows, and rows are only written by the server/webhook with the service role key.
1) Create a migration (supabase/migrations/<timestamp>_rls_payments.sql) with exactly this SQL and apply it (supabase db push, or run it in the SQL editor):
-- Fix anonymous access to public.payments
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.payments enable row level security;
-- 2) First delete any "allow all" / "true" policies on payments: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'payments';)
-- 3) Logged-out visitors get nothing
revoke all on table public.payments from anon;
-- 4) Signed-in users can read only their own rows
create policy "payments_select_own" on public.payments for select to authenticated using ((select auth.uid()) = user_id);
-- 5) No insert/update/delete policies on purpose: write these rows only from your server
-- or Stripe webhook using the service role key (never from the browser).
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
2) Find every supabase.from('payments') call and make sure it runs with a logged-in session; move anything that must read other users' rows into a server route / Edge Function using the service role key.
3) Check every other table the app uses for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/payments?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
CRITICAL: the Supabase table public.payments is readable with only the anon key. It has sensitive columns: stripe_customer_id.
Goal: logged-out visitors get nothing, signed-in users can only read their own rows, and rows are only written by the server/webhook with the service role key.
1) Create a migration (supabase/migrations/<timestamp>_rls_payments.sql) with exactly this SQL and apply it (supabase db push, or run it in the SQL editor):
-- Fix anonymous access to public.payments
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.payments enable row level security;
-- 2) First delete any "allow all" / "true" policies on payments: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'payments';)
-- 3) Logged-out visitors get nothing
revoke all on table public.payments from anon;
-- 4) Signed-in users can read only their own rows
create policy "payments_select_own" on public.payments for select to authenticated using ((select auth.uid()) = user_id);
-- 5) No insert/update/delete policies on purpose: write these rows only from your server
-- or Stripe webhook using the service role key (never from the browser).
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
2) Find every supabase.from('payments') call and make sure it runs with a logged-in session; move anything that must read other users' rows into a server route / Edge Function using the service role key.
3) Check every other table the app uses for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/payments?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Security fix: anyone can read my Supabase table "payments" without logging in. Turn on Row Level Security for it so that logged-out visitors get nothing, signed-in users can only read their own rows, and rows are only written by the server/webhook with the service role key. Use this SQL as the migration (adjust the owner column if it is named differently):
-- Fix anonymous access to public.payments
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.payments enable row level security;
-- 2) First delete any "allow all" / "true" policies on payments: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'payments';)
-- 3) Logged-out visitors get nothing
revoke all on table public.payments from anon;
-- 4) Signed-in users can read only their own rows
create policy "payments_select_own" on public.payments for select to authenticated using ((select auth.uid()) = user_id);
-- 5) No insert/update/delete policies on purpose: write these rows only from your server
-- or Stripe webhook using the service role key (never from the browser).
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
Then make sure the app still works for logged-in users, and check my other tables for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/payments?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Apply this to my project (run the SQL as a Supabase migration on the connected project), keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Security fix: anyone can read my Supabase table "payments" without logging in. Turn on Row Level Security for it so that logged-out visitors get nothing, signed-in users can only read their own rows, and rows are only written by the server/webhook with the service role key. Use this SQL as the migration (adjust the owner column if it is named differently):
-- Fix anonymous access to public.payments
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.payments enable row level security;
-- 2) First delete any "allow all" / "true" policies on payments: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'payments';)
-- 3) Logged-out visitors get nothing
revoke all on table public.payments from anon;
-- 4) Signed-in users can read only their own rows
create policy "payments_select_own" on public.payments for select to authenticated using ((select auth.uid()) = user_id);
-- 5) No insert/update/delete policies on purpose: write these rows only from your server
-- or Stripe webhook using the service role key (never from the browser).
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
Then make sure the app still works for logged-in users, and check my other tables for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/payments?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Make the change in this project (put the SQL in a new file under supabase/migrations and apply it to the connected Supabase project), keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Security fix: anyone can read my Supabase table "payments" without logging in. Turn on Row Level Security for it so that logged-out visitors get nothing, signed-in users can only read their own rows, and rows are only written by the server/webhook with the service role key. Use this SQL as the migration (adjust the owner column if it is named differently):
-- Fix anonymous access to public.payments
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.payments enable row level security;
-- 2) First delete any "allow all" / "true" policies on payments: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'payments';)
-- 3) Logged-out visitors get nothing
revoke all on table public.payments from anon;
-- 4) Signed-in users can read only their own rows
create policy "payments_select_own" on public.payments for select to authenticated using ((select auth.uid()) = user_id);
-- 5) No insert/update/delete policies on purpose: write these rows only from your server
-- or Stripe webhook using the service role key (never from the browser).
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
Then make sure the app still works for logged-in users, and check my other tables for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/payments?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
HIGH
Supabase Storage bucket "invoices" lets anyone list its files
A read-only list request on bucket "invoices" with only the anon key returned file entries. A storage.objects SELECT policy allows anonymous listing. (We did not store any file names.)
Plain-English fix
Anyone can see the list of files in your Supabase Storage bucket "invoices" with the public anon key. Remove the storage policy that lets anonymous users SELECT, and store private files under a folder named after the user's id with owner-only policies.
Supabase SQL editor (paste and run)
-- 1) Find the policy that lets anyone list files in "invoices"
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
-- 2) Drop the over-broad one(s), e.g.:
-- drop policy "<policy name>" on storage.objects;
-- 3) Owner-only access: files live under a folder named after the user id (<uid>/file.png)
create policy "invoices_read_own" on storage.objects for select to authenticated
using (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
create policy "invoices_upload_own" on storage.objects for insert to authenticated
with check (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
-- If these files are meant to be public (e.g. avatars), make the bucket public for downloads instead of adding an anon SELECT policy: public URLs work without one and nobody can list the folder.
-- Then check from a terminal:
-- curl -X POST "https://YOUR_PROJECT.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Prompt for Cursor
Supabase Storage bucket "invoices" can be listed anonymously. Write and apply a migration with this SQL (adjust the folder convention if files are not stored under <user id>/...):
-- 1) Find the policy that lets anyone list files in "invoices"
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
-- 2) Drop the over-broad one(s), e.g.:
-- drop policy "<policy name>" on storage.objects;
-- 3) Owner-only access: files live under a folder named after the user id (<uid>/file.png)
create policy "invoices_read_own" on storage.objects for select to authenticated
using (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
create policy "invoices_upload_own" on storage.objects for insert to authenticated
with check (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
-- If these files are meant to be public (e.g. avatars), make the bucket public for downloads instead of adding an anon SELECT policy: public URLs work without one and nobody can list the folder.
Then update upload code to save files at `${user.id}/<filename>`, and use createSignedUrl() for private downloads.
How to check it worked: curl -X POST "https://YOUR_PROJECT.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Supabase Storage bucket "invoices" can be listed anonymously. Write and apply a migration with this SQL (adjust the folder convention if files are not stored under <user id>/...):
-- 1) Find the policy that lets anyone list files in "invoices"
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
-- 2) Drop the over-broad one(s), e.g.:
-- drop policy "<policy name>" on storage.objects;
-- 3) Owner-only access: files live under a folder named after the user id (<uid>/file.png)
create policy "invoices_read_own" on storage.objects for select to authenticated
using (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
create policy "invoices_upload_own" on storage.objects for insert to authenticated
with check (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
-- If these files are meant to be public (e.g. avatars), make the bucket public for downloads instead of adding an anon SELECT policy: public URLs work without one and nobody can list the folder.
Then update upload code to save files at `${user.id}/<filename>`, and use createSignedUrl() for private downloads.
How to check it worked: curl -X POST "https://YOUR_PROJECT.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Security fix: anyone can list the files in my Supabase Storage bucket "invoices" without logging in. Remove the public listing policy and make files private per user (stored in a folder named after the user id, readable only by that user, shared with signed URLs). Use this SQL:
-- 1) Find the policy that lets anyone list files in "invoices"
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
-- 2) Drop the over-broad one(s), e.g.:
-- drop policy "<policy name>" on storage.objects;
-- 3) Owner-only access: files live under a folder named after the user id (<uid>/file.png)
create policy "invoices_read_own" on storage.objects for select to authenticated
using (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
create policy "invoices_upload_own" on storage.objects for insert to authenticated
with check (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
-- If these files are meant to be public (e.g. avatars), make the bucket public for downloads instead of adding an anon SELECT policy: public URLs work without one and nobody can list the folder.
How to check it worked: curl -X POST "https://YOUR_PROJECT.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Apply this to my project (run the SQL as a Supabase migration on the connected project), keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Security fix: anyone can list the files in my Supabase Storage bucket "invoices" without logging in. Remove the public listing policy and make files private per user (stored in a folder named after the user id, readable only by that user, shared with signed URLs). Use this SQL:
-- 1) Find the policy that lets anyone list files in "invoices"
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
-- 2) Drop the over-broad one(s), e.g.:
-- drop policy "<policy name>" on storage.objects;
-- 3) Owner-only access: files live under a folder named after the user id (<uid>/file.png)
create policy "invoices_read_own" on storage.objects for select to authenticated
using (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
create policy "invoices_upload_own" on storage.objects for insert to authenticated
with check (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
-- If these files are meant to be public (e.g. avatars), make the bucket public for downloads instead of adding an anon SELECT policy: public URLs work without one and nobody can list the folder.
How to check it worked: curl -X POST "https://YOUR_PROJECT.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Make the change in this project (put the SQL in a new file under supabase/migrations and apply it to the connected Supabase project), keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Security fix: anyone can list the files in my Supabase Storage bucket "invoices" without logging in. Remove the public listing policy and make files private per user (stored in a folder named after the user id, readable only by that user, shared with signed URLs). Use this SQL:
-- 1) Find the policy that lets anyone list files in "invoices"
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
-- 2) Drop the over-broad one(s), e.g.:
-- drop policy "<policy name>" on storage.objects;
-- 3) Owner-only access: files live under a folder named after the user id (<uid>/file.png)
create policy "invoices_read_own" on storage.objects for select to authenticated
using (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
create policy "invoices_upload_own" on storage.objects for insert to authenticated
with check (bucket_id = 'invoices' and (storage.foldername(name))[1] = (select auth.uid())::text);
-- If these files are meant to be public (e.g. avatars), make the bucket public for downloads instead of adding an anon SELECT policy: public URLs work without one and nobody can list the folder.
How to check it worked: curl -X POST "https://YOUR_PROJECT.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
MEDIUM
Missing Content-Security-Policy
No CSP header. A baseline CSP reduces XSS impact.
Plain-English fix
Content-Security-Policy is missing. A CSP limits which scripts can run, which reduces the damage of an XSS bug. Start in report-only mode, then enforce.
Prompt for Cursor
Add a Content-Security-Policy header. Start with Content-Security-Policy-Report-Only so nothing breaks:
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://*.supabase.co; frame-ancestors 'none'; base-uri 'self'; object-src 'none'
List every third-party domain the app actually loads (analytics, fonts, Stripe, Supabase) and add them to the right directive. Check the browser console for violations, then switch to the enforcing Content-Security-Policy header (vercel.json headers, netlify.toml, or next.config.js headers()).
How to check it worked: curl -sI https://your-site.com | grep -i content-security-policy
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Add a Content-Security-Policy header. Start with Content-Security-Policy-Report-Only so nothing breaks:
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://*.supabase.co; frame-ancestors 'none'; base-uri 'self'; object-src 'none'
List every third-party domain the app actually loads (analytics, fonts, Stripe, Supabase) and add them to the right directive. Check the browser console for violations, then switch to the enforcing Content-Security-Policy header (vercel.json headers, netlify.toml, or next.config.js headers()).
How to check it worked: curl -sI https://your-site.com | grep -i content-security-policy
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Add a Content-Security-Policy header to my site to reduce XSS risk. Start in report-only mode with default-src 'self', add the third-party domains my app really uses (Supabase, Stripe, fonts, analytics), and tell me when it's safe to enforce it.
How to check it worked: curl -sI https://your-site.com | grep -i content-security-policy
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Add a Content-Security-Policy header to my site to reduce XSS risk. Start in report-only mode with default-src 'self', add the third-party domains my app really uses (Supabase, Stripe, fonts, analytics), and tell me when it's safe to enforce it.
How to check it worked: curl -sI https://your-site.com | grep -i content-security-policy
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Add a Content-Security-Policy header to my site to reduce XSS risk. Start in report-only mode with default-src 'self', add the third-party domains my app really uses (Supabase, Stripe, fonts, analytics), and tell me when it's safe to enforce it.
How to check it worked: curl -sI https://your-site.com | grep -i content-security-policy
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
MEDIUM
jQuery 3.4.1 has known vulnerabilities (CVE-2020-11022)
Your site ships jQuery 3.4.1 (seen in index-Dk3x9QpA.js). Known advisories for this version: CVE-2020-11022 / CVE-2020-11023: XSS when untrusted HTML is passed to .html(), .append() and similar methods (fixed in 3.5.0). Version detected from the shipped files; it can be wrong if the banner was edited.
Evidence: jQuery 3.4.1
Plain-English fix
Your site ships jQuery 3.4.1, which has known security advisories (CVE-2020-11022). Upgrade it (fixed in 3.5.0) and redeploy.
Prompt for Cursor
Upgrade a vulnerable client library: jQuery 3.4.1 is in the deployed bundle.
Advisories:
- CVE-2020-11022 (fixed in 3.5.0): https://nvd.nist.gov/vuln/detail/CVE-2020-11022
Find where jQuery comes from: a package.json dependency ("npm ls jquery" shows which package pulls it in), a CDN <script> tag, or a WordPress/theme bundle. Upgrade to 3.7.1 or later (npm install jquery@latest, or update the CDN URL), run "npm audit" for other advisories, rebuild and redeploy. Read the jQuery 3.5 upgrade guide: htmlPrefilter changed, so test pages that inject HTML.
How to check it worked: View the live JS in DevTools > Sources and confirm the jQuery version banner shows the new version, then re-scan.
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Upgrade a vulnerable client library: jQuery 3.4.1 is in the deployed bundle.
Advisories:
- CVE-2020-11022 (fixed in 3.5.0): https://nvd.nist.gov/vuln/detail/CVE-2020-11022
Find where jQuery comes from: a package.json dependency ("npm ls jquery" shows which package pulls it in), a CDN <script> tag, or a WordPress/theme bundle. Upgrade to 3.7.1 or later (npm install jquery@latest, or update the CDN URL), run "npm audit" for other advisories, rebuild and redeploy. Read the jQuery 3.5 upgrade guide: htmlPrefilter changed, so test pages that inject HTML.
How to check it worked: View the live JS in DevTools > Sources and confirm the jQuery version banner shows the new version, then re-scan.
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
My live site uses jQuery 3.4.1, which has known security vulnerabilities (CVE-2020-11022). Upgrade it to the latest safe version, fix anything the upgrade breaks, and tell me how to confirm the new version is live.
How to check it worked: View the live JS in DevTools > Sources and confirm the jQuery version banner shows the new version, then re-scan.
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
My live site uses jQuery 3.4.1, which has known security vulnerabilities (CVE-2020-11022). Upgrade it to the latest safe version, fix anything the upgrade breaks, and tell me how to confirm the new version is live.
How to check it worked: View the live JS in DevTools > Sources and confirm the jQuery version banner shows the new version, then re-scan.
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
My live site uses jQuery 3.4.1, which has known security vulnerabilities (CVE-2020-11022). Upgrade it to the latest safe version, fix anything the upgrade breaks, and tell me how to confirm the new version is live.
How to check it worked: View the live JS in DevTools > Sources and confirm the jQuery version banner shows the new version, then re-scan.
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
MEDIUM
Source map publicly downloadable
https://your-app.lovable.app/assets/index-Dk3x9QpA.js.map is reachable and looks like a source map. Disable production source maps.
JavaScript source maps are downloadable, which hands out your original source code (comments, file structure, sometimes secrets). Turn them off for production.
Prompt for Cursor
Disable public production source maps: Vite build.sourcemap = false (or "hidden" and upload to your error tracker); Next.js productionBrowserSourceMaps: false; CRA GENERATE_SOURCEMAP=false. Make sure no *.map files are in the deploy output.
How to check it worked: Open one of the .map URLs from the report: it should be 404.
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Disable public production source maps: Vite build.sourcemap = false (or "hidden" and upload to your error tracker); Next.js productionBrowserSourceMaps: false; CRA GENERATE_SOURCEMAP=false. Make sure no *.map files are in the deploy output.
How to check it worked: Open one of the .map URLs from the report: it should be 404.
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Turn off source maps for production builds so my original source code is not downloadable, and redeploy.
How to check it worked: Open one of the .map URLs from the report: it should be 404.
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Turn off source maps for production builds so my original source code is not downloadable, and redeploy.
How to check it worked: Open one of the .map URLs from the report: it should be 404.
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Turn off source maps for production builds so my original source code is not downloadable, and redeploy.
How to check it worked: Open one of the .map URLs from the report: it should be 404.
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
MEDIUM
Supabase table "posts" is publicly readable (fine only if meant to be public)
A read-only GET to https://abcdefghijklmnopqrst.supabase.co/rest/v1/posts?limit=1 with only the public anon key (which ships in your JavaScript) returned a row, so anyone can download this table. Row Level Security is off or has a policy that allows anonymous reads. (We did not store or display any row values.)
Plain-English fix
Anyone can download rows from "posts" using the public anon key that ships in your JavaScript. Turn on Row Level Security for it and add policies so that everyone can read it (it is public content), but only the author can create, edit or delete their own rows.
Supabase SQL editor (paste and run)
-- Fix anonymous access to public.posts
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.posts enable row level security;
-- 2) First delete any "allow all" / "true" policies on posts: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'posts';)
-- 3) Everyone may read (add "using (published = true)" instead of "true" if you have drafts)
create policy "posts_public_read" on public.posts for select to anon, authenticated using (true);
-- 4) Only the author can write
create policy "posts_insert_own" on public.posts for insert to authenticated with check ((select auth.uid()) = author_id);
create policy "posts_update_own" on public.posts for update to authenticated using ((select auth.uid()) = author_id) with check ((select auth.uid()) = author_id);
create policy "posts_delete_own" on public.posts for delete to authenticated using ((select auth.uid()) = author_id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
-- Then check from a terminal:
-- curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/posts?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Prompt for Cursor
CRITICAL: the Supabase table public.posts is readable with only the anon key.
Goal: everyone can read it (it is public content), but only the author can create, edit or delete their own rows.
1) Create a migration (supabase/migrations/<timestamp>_rls_posts.sql) with exactly this SQL and apply it (supabase db push, or run it in the SQL editor):
-- Fix anonymous access to public.posts
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.posts enable row level security;
-- 2) First delete any "allow all" / "true" policies on posts: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'posts';)
-- 3) Everyone may read (add "using (published = true)" instead of "true" if you have drafts)
create policy "posts_public_read" on public.posts for select to anon, authenticated using (true);
-- 4) Only the author can write
create policy "posts_insert_own" on public.posts for insert to authenticated with check ((select auth.uid()) = author_id);
create policy "posts_update_own" on public.posts for update to authenticated using ((select auth.uid()) = author_id) with check ((select auth.uid()) = author_id);
create policy "posts_delete_own" on public.posts for delete to authenticated using ((select auth.uid()) = author_id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
2) Find every supabase.from('posts') call and make sure it runs with a logged-in session; move anything that must read other users' rows into a server route / Edge Function using the service role key.
3) Check every other table the app uses for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/posts?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
CRITICAL: the Supabase table public.posts is readable with only the anon key.
Goal: everyone can read it (it is public content), but only the author can create, edit or delete their own rows.
1) Create a migration (supabase/migrations/<timestamp>_rls_posts.sql) with exactly this SQL and apply it (supabase db push, or run it in the SQL editor):
-- Fix anonymous access to public.posts
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.posts enable row level security;
-- 2) First delete any "allow all" / "true" policies on posts: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'posts';)
-- 3) Everyone may read (add "using (published = true)" instead of "true" if you have drafts)
create policy "posts_public_read" on public.posts for select to anon, authenticated using (true);
-- 4) Only the author can write
create policy "posts_insert_own" on public.posts for insert to authenticated with check ((select auth.uid()) = author_id);
create policy "posts_update_own" on public.posts for update to authenticated using ((select auth.uid()) = author_id) with check ((select auth.uid()) = author_id);
create policy "posts_delete_own" on public.posts for delete to authenticated using ((select auth.uid()) = author_id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
2) Find every supabase.from('posts') call and make sure it runs with a logged-in session; move anything that must read other users' rows into a server route / Edge Function using the service role key.
3) Check every other table the app uses for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/posts?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Security fix: anyone can read my Supabase table "posts" without logging in. Turn on Row Level Security for it so that everyone can read it (it is public content), but only the author can create, edit or delete their own rows. Use this SQL as the migration (adjust the owner column if it is named differently):
-- Fix anonymous access to public.posts
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.posts enable row level security;
-- 2) First delete any "allow all" / "true" policies on posts: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'posts';)
-- 3) Everyone may read (add "using (published = true)" instead of "true" if you have drafts)
create policy "posts_public_read" on public.posts for select to anon, authenticated using (true);
-- 4) Only the author can write
create policy "posts_insert_own" on public.posts for insert to authenticated with check ((select auth.uid()) = author_id);
create policy "posts_update_own" on public.posts for update to authenticated using ((select auth.uid()) = author_id) with check ((select auth.uid()) = author_id);
create policy "posts_delete_own" on public.posts for delete to authenticated using ((select auth.uid()) = author_id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
Then make sure the app still works for logged-in users, and check my other tables for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/posts?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Apply this to my project (run the SQL as a Supabase migration on the connected project), keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Security fix: anyone can read my Supabase table "posts" without logging in. Turn on Row Level Security for it so that everyone can read it (it is public content), but only the author can create, edit or delete their own rows. Use this SQL as the migration (adjust the owner column if it is named differently):
-- Fix anonymous access to public.posts
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.posts enable row level security;
-- 2) First delete any "allow all" / "true" policies on posts: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'posts';)
-- 3) Everyone may read (add "using (published = true)" instead of "true" if you have drafts)
create policy "posts_public_read" on public.posts for select to anon, authenticated using (true);
-- 4) Only the author can write
create policy "posts_insert_own" on public.posts for insert to authenticated with check ((select auth.uid()) = author_id);
create policy "posts_update_own" on public.posts for update to authenticated using ((select auth.uid()) = author_id) with check ((select auth.uid()) = author_id);
create policy "posts_delete_own" on public.posts for delete to authenticated using ((select auth.uid()) = author_id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
Then make sure the app still works for logged-in users, and check my other tables for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/posts?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Make the change in this project (put the SQL in a new file under supabase/migrations and apply it to the connected Supabase project), keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Security fix: anyone can read my Supabase table "posts" without logging in. Turn on Row Level Security for it so that everyone can read it (it is public content), but only the author can create, edit or delete their own rows. Use this SQL as the migration (adjust the owner column if it is named differently):
-- Fix anonymous access to public.posts
-- 1) Turn on Row Level Security (denies everything until a policy allows it)
alter table public.posts enable row level security;
-- 2) First delete any "allow all" / "true" policies on posts: Dashboard > Authentication > Policies
-- (list them with: select policyname, roles, cmd, qual from pg_policies where tablename = 'posts';)
-- 3) Everyone may read (add "using (published = true)" instead of "true" if you have drafts)
create policy "posts_public_read" on public.posts for select to anon, authenticated using (true);
-- 4) Only the author can write
create policy "posts_insert_own" on public.posts for insert to authenticated with check ((select auth.uid()) = author_id);
create policy "posts_update_own" on public.posts for update to authenticated using ((select auth.uid()) = author_id) with check ((select auth.uid()) = author_id);
create policy "posts_delete_own" on public.posts for delete to authenticated using ((select auth.uid()) = author_id);
-- Check: every table in public should show rowsecurity = true
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
Then make sure the app still works for logged-in users, and check my other tables for the same problem.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/posts?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
LOW
Missing X-Frame-Options / frame-ancestors
Page may be embeddable in clickjacking iframes.
Plain-English fix
X-Frame-Options / frame-ancestors is missing, so other sites can load yours in a hidden iframe (clickjacking). Set X-Frame-Options: DENY or CSP frame-ancestors 'none'.
Prompt for Cursor
Add the response header "X-Frame-Options: DENY" to every HTML response.
- Vercel: add it under "headers" for source "/(.*)" in vercel.json
- Netlify: add it to _headers or netlify.toml
- Next.js: add it in next.config.js async headers()
- Express: res.setHeader in a global middleware
Do not change anything else.
How to check it worked: curl -sI https://your-site.com | grep -i "X-Frame-Options"
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Add the response header "X-Frame-Options: DENY" to every HTML response.
- Vercel: add it under "headers" for source "/(.*)" in vercel.json
- Netlify: add it to _headers or netlify.toml
- Next.js: add it in next.config.js async headers()
- Express: res.setHeader in a global middleware
Do not change anything else.
How to check it worked: curl -sI https://your-site.com | grep -i "X-Frame-Options"
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Add the security header "X-Frame-Options: DENY" to every page of my site, using the hosting config (vercel.json, netlify.toml or next.config.js). Don't change anything else.
How to check it worked: curl -sI https://your-site.com | grep -i "X-Frame-Options"
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Add the security header "X-Frame-Options: DENY" to every page of my site, using the hosting config (vercel.json, netlify.toml or next.config.js). Don't change anything else.
How to check it worked: curl -sI https://your-site.com | grep -i "X-Frame-Options"
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Add the security header "X-Frame-Options: DENY" to every page of my site, using the hosting config (vercel.json, netlify.toml or next.config.js). Don't change anything else.
How to check it worked: curl -sI https://your-site.com | grep -i "X-Frame-Options"
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
LOW
Missing X-Content-Type-Options
Add nosniff to prevent MIME sniffing.
Plain-English fix
X-Content-Type-Options: nosniff is missing. Add it so browsers do not guess file types (MIME sniffing).
Prompt for Cursor
Add the response header "X-Content-Type-Options: nosniff" to every HTML response.
- Vercel: add it under "headers" for source "/(.*)" in vercel.json
- Netlify: add it to _headers or netlify.toml
- Next.js: add it in next.config.js async headers()
- Express: res.setHeader in a global middleware
Do not change anything else.
How to check it worked: curl -sI https://your-site.com | grep -i "X-Content-Type-Options"
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Add the response header "X-Content-Type-Options: nosniff" to every HTML response.
- Vercel: add it under "headers" for source "/(.*)" in vercel.json
- Netlify: add it to _headers or netlify.toml
- Next.js: add it in next.config.js async headers()
- Express: res.setHeader in a global middleware
Do not change anything else.
How to check it worked: curl -sI https://your-site.com | grep -i "X-Content-Type-Options"
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Add the security header "X-Content-Type-Options: nosniff" to every page of my site, using the hosting config (vercel.json, netlify.toml or next.config.js). Don't change anything else.
How to check it worked: curl -sI https://your-site.com | grep -i "X-Content-Type-Options"
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Add the security header "X-Content-Type-Options: nosniff" to every page of my site, using the hosting config (vercel.json, netlify.toml or next.config.js). Don't change anything else.
How to check it worked: curl -sI https://your-site.com | grep -i "X-Content-Type-Options"
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Add the security header "X-Content-Type-Options: nosniff" to every page of my site, using the hosting config (vercel.json, netlify.toml or next.config.js). Don't change anything else.
How to check it worked: curl -sI https://your-site.com | grep -i "X-Content-Type-Options"
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
LOW
Missing meta description
No meta description found. Add a one-sentence summary for search snippets.
Plain-English fix
No meta description. Add a one-sentence summary for search snippets and social previews.
Prompt for Cursor
Add <meta name="description" content="..."> (140-160 chars) to index.html, or metadata.description in the Next.js root layout.
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Add <meta name="description" content="..."> (140-160 chars) to index.html, or metadata.description in the Next.js root layout.
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Add a one-sentence meta description that explains what my app does, for Google and link previews.
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Add a one-sentence meta description that explains what my app does, for Google and link previews.
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Add a one-sentence meta description that explains what my app does, for Google and link previews.
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
LOW
CORS reflects any origin (without credentials)
A GET to https://your-app.lovable.app/ with "Origin: https://evil.example" came back with Access-Control-Allow-Origin: https://evil.example. Cookies are not allowed, so this is low risk for public pages, but it usually means a catch-all CORS setting that could become dangerous if credentials are switched on or private endpoints share it.
Your server copies any Origin back into Access-Control-Allow-Origin. Without credentials this is low risk for public pages, but replace the catch-all with an allowlist so it never becomes a problem on private endpoints.
Prompt for Cursor
Tighten CORS: the server reflects any Origin header into Access-Control-Allow-Origin. Replace the reflect-all setting with an allowlist of your own domains (or omit CORS headers entirely on HTML pages that do not need them), add "Vary: Origin", and make sure no route combines a reflected origin with Access-Control-Allow-Credentials: true.
How to check it worked: curl -s -o /dev/null -D - -H "Origin: https://evil.example" https://your-site.com | grep -i access-control
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Tighten CORS: the server reflects any Origin header into Access-Control-Allow-Origin. Replace the reflect-all setting with an allowlist of your own domains (or omit CORS headers entirely on HTML pages that do not need them), add "Vary: Origin", and make sure no route combines a reflected origin with Access-Control-Allow-Credentials: true.
How to check it worked: curl -s -o /dev/null -D - -H "Origin: https://evil.example" https://your-site.com | grep -i access-control
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
My site answers CORS requests from any website. Limit Access-Control-Allow-Origin to my own domains (or remove CORS headers where they are not needed) and make sure credentials are never allowed for other origins.
How to check it worked: curl -s -o /dev/null -D - -H "Origin: https://evil.example" https://your-site.com | grep -i access-control
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
My site answers CORS requests from any website. Limit Access-Control-Allow-Origin to my own domains (or remove CORS headers where they are not needed) and make sure credentials are never allowed for other origins.
How to check it worked: curl -s -o /dev/null -D - -H "Origin: https://evil.example" https://your-site.com | grep -i access-control
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
My site answers CORS requests from any website. Limit Access-Control-Allow-Origin to my own domains (or remove CORS headers where they are not needed) and make sure credentials are never allowed for other origins.
How to check it worked: curl -s -o /dev/null -D - -H "Origin: https://evil.example" https://your-site.com | grep -i access-control
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
LOW
Supabase schema lists 6 tables to anonymous users
https://abcdefghijklmnopqrst.supabase.co/rest/v1/ returned the API schema using only the public anon key, revealing table names (e.g. profiles, payments, posts, messages, waitlist, admin_settings). Column names that look sensitive are also visible: profiles (email, phone, stripe_customer_id); payments (stripe_customer_id); waitlist (email). Names alone are not a breach, but they map out your data for attackers, so every table needs RLS.
Plain-English fix
Your Supabase table and column names are visible with the public anon key. That alone is normal for Supabase, but it tells attackers exactly where to look, so every table must have RLS and internal tables should not be in the public API.
Supabase SQL editor (paste and run)
-- 1) Tables without Row Level Security (fix every row this returns)
select tablename from pg_tables where schemaname = 'public' and not rowsecurity;
-- 2) Policies that allow everyone (qual = 'true') — review each
select tablename, policyname, roles, cmd, qual from pg_policies where schemaname = 'public' and (qual = 'true' or 'anon' = any(roles));
-- 3) For internal tables the browser never reads:
-- revoke all on table public.<name> from anon, authenticated;
-- Then check from a terminal:
-- curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Prompt for Cursor
The Supabase REST schema (/rest/v1/) lists our tables to anonymous users (profiles, payments, posts, messages, waitlist, admin_settings).
1) Enable RLS on every table in public and add auth.uid()-scoped policies (find gaps with: select tablename from pg_tables where schemaname='public' and not rowsecurity;).
2) Move internal/admin tables to a private schema that is not in "Exposed schemas" (Project Settings > API), or revoke anon access: revoke all on table public.<name> from anon;
3) Write the SQL as a migration file and apply it.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
The Supabase REST schema (/rest/v1/) lists our tables to anonymous users (profiles, payments, posts, messages, waitlist, admin_settings).
1) Enable RLS on every table in public and add auth.uid()-scoped policies (find gaps with: select tablename from pg_tables where schemaname='public' and not rowsecurity;).
2) Move internal/admin tables to a private schema that is not in "Exposed schemas" (Project Settings > API), or revoke anon access: revoke all on table public.<name> from anon;
3) Write the SQL as a migration file and apply it.
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Anyone can see my Supabase table names. Make sure Row Level Security is on for every table with policies so users only see their own data, and stop exposing internal/admin tables to the public API (revoke access for the anon role).
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Apply this to my project (run the SQL as a Supabase migration on the connected project), keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Anyone can see my Supabase table names. Make sure Row Level Security is on for every table with policies so users only see their own data, and stop exposing internal/admin tables to the public API (revoke access for the anon role).
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Make the change in this project (put the SQL in a new file under supabase/migrations and apply it to the connected Supabase project), keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Anyone can see my Supabase table names. Make sure Row Level Security is on for every table with policies so users only see their own data, and stop exposing internal/admin tables to the public API (revoke access for the anon role).
How to check it worked: curl "https://abcdefghijklmnopqrst.supabase.co/rest/v1/?select=*&limit=1" -H "apikey: YOUR_ANON_KEY" # expect [] or a 401/permission error
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
LOW
Supabase Storage bucket list visible to anonymous users (1)
https://abcdefghijklmnopqrst.supabase.co/storage/v1/bucket returned your bucket names with only the anon key: invoices. Public buckets serve any file to anyone who knows its path.
Plain-English fix
Anyone can see your Supabase Storage bucket list with the public anon key. Remove the storage policy that lets anonymous users SELECT, and store private files under a folder named after the user's id with owner-only policies.
Supabase SQL editor (paste and run)
-- Who can list buckets? Drop any policy that lets anon read storage.buckets:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'buckets';
-- drop policy "<policy name>" on storage.buckets;
-- Then check object policies (listing files) the same way:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
-- Then check from a terminal:
-- curl -X POST "https://abcdefghijklmnopqrst.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Prompt for Cursor
Supabase Storage bucket metadata can be listed anonymously. Write and apply a migration with this SQL (adjust the folder convention if files are not stored under <user id>/...):
-- Who can list buckets? Drop any policy that lets anon read storage.buckets:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'buckets';
-- drop policy "<policy name>" on storage.buckets;
-- Then check object policies (listing files) the same way:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
Then update upload code to save files at `${user.id}/<filename>`, and use createSignedUrl() for private downloads.
How to check it worked: curl -X POST "https://abcdefghijklmnopqrst.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Supabase Storage bucket metadata can be listed anonymously. Write and apply a migration with this SQL (adjust the folder convention if files are not stored under <user id>/...):
-- Who can list buckets? Drop any policy that lets anon read storage.buckets:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'buckets';
-- drop policy "<policy name>" on storage.buckets;
-- Then check object policies (listing files) the same way:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
Then update upload code to save files at `${user.id}/<filename>`, and use createSignedUrl() for private downloads.
How to check it worked: curl -X POST "https://abcdefghijklmnopqrst.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Security fix: anyone can list my Supabase Storage buckets without logging in. Remove the public listing policy and make files private per user (stored in a folder named after the user id, readable only by that user, shared with signed URLs). Use this SQL:
-- Who can list buckets? Drop any policy that lets anon read storage.buckets:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'buckets';
-- drop policy "<policy name>" on storage.buckets;
-- Then check object policies (listing files) the same way:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
How to check it worked: curl -X POST "https://abcdefghijklmnopqrst.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Apply this to my project (run the SQL as a Supabase migration on the connected project), keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Security fix: anyone can list my Supabase Storage buckets without logging in. Remove the public listing policy and make files private per user (stored in a folder named after the user id, readable only by that user, shared with signed URLs). Use this SQL:
-- Who can list buckets? Drop any policy that lets anon read storage.buckets:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'buckets';
-- drop policy "<policy name>" on storage.buckets;
-- Then check object policies (listing files) the same way:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
How to check it worked: curl -X POST "https://abcdefghijklmnopqrst.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Make the change in this project (put the SQL in a new file under supabase/migrations and apply it to the connected Supabase project), keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Security fix: anyone can list my Supabase Storage buckets without logging in. Remove the public listing policy and make files private per user (stored in a folder named after the user id, readable only by that user, shared with signed URLs). Use this SQL:
-- Who can list buckets? Drop any policy that lets anon read storage.buckets:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'buckets';
-- drop policy "<policy name>" on storage.buckets;
-- Then check object policies (listing files) the same way:
select policyname, roles, cmd, qual from pg_policies where schemaname = 'storage' and tablename = 'objects';
How to check it worked: curl -X POST "https://abcdefghijklmnopqrst.supabase.co/storage/v1/object/list/invoices" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY" -H "Content-Type: application/json" -d '{"prefix":"","limit":1}' # expect []
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
INFO
Supabase project URL in client
Supabase URL https://abcdefghijklmnopqrst.supabase.co is public by design; ensure RLS protects your tables.
Evidence: https:…e.co
Plain-English fix
Your Supabase URL and anon/publishable key in the browser are normal. They are only safe if Row Level Security is on for every table, so check that.
Supabase SQL editor (paste and run)
-- Tables in the public schema without Row Level Security:
select tablename from pg_tables where schemaname = 'public' and not rowsecurity;
-- For each one: alter table public.<name> enable row level security; then add policies using (select auth.uid()).
Prompt for Cursor
The Supabase URL + anon/publishable key are public by design. Audit RLS: list tables without RLS (select tablename from pg_tables where schemaname='public' and not rowsecurity;), enable it on each, and add policies scoped to auth.uid(). Confirm no service_role key is in client code.
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
The Supabase URL + anon/publishable key are public by design. Audit RLS: list tables without RLS (select tablename from pg_tables where schemaname='public' and not rowsecurity;), enable it on each, and add policies scoped to auth.uid(). Confirm no service_role key is in client code.
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Keep my Supabase URL and anon key, but check that Row Level Security is enabled on every table and that each table has policies so users can only see their own data. Show me any table without RLS.
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Keep my Supabase URL and anon key, but check that Row Level Security is enabled on every table and that each table has policies so users can only see their own data. Show me any table without RLS.
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Keep my Supabase URL and anon key, but check that Row Level Security is enabled on every table and that each table has policies so users can only see their own data. Show me any table without RLS.
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
INFO
Supabase anon key in client (expected)
Anon/publishable keys are designed for the browser, but only safe if RLS is enabled on every table. Redacted: eyJhbG…real.
Evidence: eyJhbG…real
Plain-English fix
A JWT (signed token) is hard-coded in your client code. If it is anything other than a public anon key, it gives whoever copies it the same access. Check what it is and move privileged tokens server-side.
Prompt for Cursor
A hard-coded JWT is in the client bundle. Decode it (jwt.io or base64 the middle part) and check its role/claims. If it is anything other than a public anon key (e.g. service_role, admin, a long-lived user/session token), remove it from client code, move the calls that need it to a server route, and revoke/rotate it with whoever issued it.
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
A hard-coded JWT is in the client bundle. Decode it (jwt.io or base64 the middle part) and check its role/claims. If it is anything other than a public anon key (e.g. service_role, admin, a long-lived user/session token), remove it from client code, move the calls that need it to a server route, and revoke/rotate it with whoever issued it.
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
A hard-coded login/API token (JWT) is in my app's browser code. Find it, tell me what it is for, and if it is not a public anon key move it to a secure backend function and remove it from the client.
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
A hard-coded login/API token (JWT) is in my app's browser code. Find it, tell me what it is for, and if it is not a public anon key move it to a secure backend function and remove it from the client.
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
A hard-coded login/API token (JWT) is in my app's browser code. Find it, tell me what it is for, and if it is not a public anon key move it to a secure backend function and remove it from the client.
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
INFO
No security.txt (how to report a vulnerability)
https://your-app.lovable.app/.well-known/security.txt was not found. It is a small text file (RFC 9116) that tells people who find a security bug how to reach you, instead of posting it publicly.
Plain-English fix
Add a /.well-known/security.txt file with a Contact email and an Expires date, so people who find a bug can tell you privately instead of posting it.
Prompt for Cursor
Add a security.txt file (RFC 9116) served at /.well-known/security.txt as text/plain:
Contact: mailto:security@your-domain.com
Expires: <a date under 1 year from today, ISO 8601, e.g. 2027-10-01T00:00:00.000Z>
Preferred-Languages: en
Canonical: https://your-app.lovable.app/.well-known/security.txt
Put it in the static folder that is served as-is (public/.well-known/security.txt for Vite, Next.js and most hosts), make sure the router does not swallow /.well-known/, and redeploy.
How to check it worked: curl -s https://your-app.lovable.app/.well-known/security.txt
Work in this repo: find every affected file, make the smallest safe change, show me the diff, and list the manual steps I still need to do outside the code.
Prompt for Claude Code
Add a security.txt file (RFC 9116) served at /.well-known/security.txt as text/plain:
Contact: mailto:security@your-domain.com
Expires: <a date under 1 year from today, ISO 8601, e.g. 2027-10-01T00:00:00.000Z>
Preferred-Languages: en
Canonical: https://your-app.lovable.app/.well-known/security.txt
Put it in the static folder that is served as-is (public/.well-known/security.txt for Vite, Next.js and most hosts), make sure the router does not swallow /.well-known/, and redeploy.
How to check it worked: curl -s https://your-app.lovable.app/.well-known/security.txt
Search the repo (grep) for every affected file, make the edits, run the build and any tests to confirm nothing broke, then summarize what you changed and the manual steps left for me.
Prompt for Lovable
Add a security.txt file at /.well-known/security.txt on my site with a Contact email (ask me which one), an Expires date less than a year from today, Preferred-Languages: en and a Canonical URL. It must be served as plain text.
How to check it worked: curl -s https://your-app.lovable.app/.well-known/security.txt
Apply this to my project, keep the app working for logged-in users, then tell me in plain words anything I must do myself outside Lovable.
Prompt for Bolt
Add a security.txt file at /.well-known/security.txt on my site with a Contact email (ask me which one), an Expires date less than a year from today, Preferred-Languages: en and a Canonical URL. It must be served as plain text.
How to check it worked: curl -s https://your-app.lovable.app/.well-known/security.txt
Make the change in this project, keep existing features working, and list anything I must do myself (dashboards, key rotation).
Prompt for v0
Add a security.txt file at /.well-known/security.txt on my site with a Contact email (ask me which one), an Expires date less than a year from today, Preferred-Languages: en and a Canonical URL. It must be served as plain text.
How to check it worked: curl -s https://your-app.lovable.app/.well-known/security.txt
Apply it to this Next.js project (server-only code in route handlers or server actions, secrets in env vars without NEXT_PUBLIC_), show which files you changed, and list any steps I must do outside v0.
Get yours: free scan
Paste your live URL and see your grade and every biggest risk free in about 30 seconds. The full report like this one is a one-time $29 with a 14-day refund, and a free re-check after you fix.
We only see what your live site sends to a browser. We don't see your git history, server code or server-side environment variables.
We don't log in, so we can't test what one user can see of another user's data. Do the two-account test yourself: sign up as user A and user B, then as A try to open, edit or delete B's records (change IDs in URLs and API calls).
Admin pages that only exist in client-side routing (for example /admin in a React app) look like your homepage to us. Make sure the data behind them is protected.
Vulnerable-library checks only know a short list (jQuery, lodash, AngularJS, Bootstrap, Next.js) and rely on version banners in your shipped files. Run npm audit for everything else.
The optional code scan only looks for secrets and risky committed files. It doesn't review your code's logic, checks only the last 100 commits of public GitHub repos, and can miss secrets that don't match a known pattern.
Passive read-only scan. Not a pentest. Fix prompts are templates written for each finding; review changes before you ship them.