⚠ EXAMPLE REPORT FOR A FICTIONAL APP · "HabitLoop" isn't real, and neither are these keys
VIBECHECK · FULL REPORT · SAMPLE

HabitLoop: 2 launch blockers, then you're close

Target: https://habitloop-demo.example + github.com/example-org/habitloop

HabitLoop is a habit tracker built with Lovable on Supabase. The good news: the app works and most of the basics are in place. The bad news: two problems would let a stranger read or wipe every user's habits. Fix those two first (about 10 minutes), then work down the list.

2
critical
1
high
2
medium
1
low
1
info
critical
Your Supabase service_role key ships inside the JavaScript every visitor downloads. That key skips all database security, so anyone can read, edit or delete every user's data.
supabase_service_role · https://habitloop-demo.example/assets/index-4f8a2c.js
Evidence: sbp_serv…[masked]…9Qx
1. Supabase dashboard → Project Settings → API → roll the service_role key NOW.
2. Remove it from the frontend:
   // src/lib/supabase.ts
   - createClient(URL, import.meta.env.VITE_SUPABASE_SERVICE_ROLE_KEY)
   + createClient(URL, import.meta.env.VITE_SUPABASE_ANON_KEY)
3. Anything that truly needs admin rights goes in a Supabase Edge Function,
   reading the key from Deno.env.get("SUPABASE_SERVICE_ROLE_KEY").
critical
The habit_checkins and profiles tables can be read by anyone holding the public anon key (which is in your page source). Row Level Security looks switched off.
supabase_open_read · https://habitloop-demo.example
Evidence: hab…[masked]…ins, pro…[masked]…les
-- Supabase SQL editor
alter table public.habit_checkins enable row level security;
alter table public.profiles enable row level security;

create policy "own checkins" on public.habit_checkins
  for all using (auth.uid() = user_id) with check (auth.uid() = user_id);
create policy "own profile" on public.profiles
  for all using (auth.uid() = id) with check (auth.uid() = id);
high
A .env file is committed to the repo. Every secret in it is now in git history, so anyone who can see the repo (or a fork of it) has your keys.
committed_env · github.com/example-org/habitloop · .env
Evidence: .env
git rm --cached .env
echo ".env" >> .gitignore
cp .env .env.example   # then blank out the values in .env.example
git commit -m "Stop tracking .env"
# Rotate every key that was in the file: deleting it doesn't remove it from history.
medium
There's no Content-Security-Policy header, so if a script injection ever slips in, the browser will happily run it.
missing_security_header · https://habitloop-demo.example
Evidence: content-security-policy
# public/_headers (Netlify / Cloudflare Pages) or your host's header config
/*
  Content-Security-Policy: default-src 'self'; connect-src 'self' https://*.supabase.co; img-src 'self' data: https:; frame-ancestors 'none'
medium
The login session cookie is missing the Secure and HttpOnly flags, so it can leak over plain HTTP and be read by JavaScript.
insecure_cookie · https://habitloop-demo.example
Evidence: sb-auth-token=…; Path=/
// wherever you set the cookie (server / edge function)
Set-Cookie: sb-auth-token=...; Path=/; Secure; HttpOnly; SameSite=Lax
low
Other sites can load your app in an invisible frame (a trick called clickjacking).
missing_security_header · https://habitloop-demo.example
Evidence: x-frame-options
# public/_headers
/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
info
Supabase URL and anon key found in the browser. That's normal and fine, as long as RLS is on for every table (see the critical finding above).
supabase_config_in_client · https://habitloop-demo.example
Evidence: hab…supabase.co / anon=eyJh…[masked]
No action needed once RLS is enabled. Keep only the anon key in VITE_* variables.
✓ PASSED

Scan ID sample-0000 · vibecheckhq.com
This is automated guidance, not a guarantee of security. It's not a pentest and won't catch everything.

What would your app's report say?

The free mini-scan flags critical issues in about 60 seconds. The full report gets you every finding, every fix, and one re-scan.