Advisory VV-003critical2026-08
Supabase service_role / secret key shipped to the browser
The service_role (or new sb_secret_) key bypasses every RLS policy. If it is in your JavaScript bundle, your whole database is public.
What it is
Supabase: secret and service role keys "are never safe to expose because they bypass RLS." The service_role Postgres role has the BYPASSRLS attribute.
The 30,998-app scan found 52 apps with a key that can spend money or read a whole database in their JavaScript, including a Supabase service_role.
How to check
- Open your live site, DevTools → Sources, and search the bundle for "service_role" or "sb_secret_".
- Decode any JWT you find (jwt.io or the snippet below): a role claim of service_role is critical; anon/authenticated is expected.
How to fix
- Rotate immediately: create a new secret key in Settings → API Keys, replace it everywhere, then retire or deactivate the old one.
- Move code that needs elevated access into an Edge Function or server route. Keep only the publishable/anon key in the browser.
// Decode a JWT payload found in your bundle (no verification needed to see the role)
const payload = JSON.parse(atob(token.split('.')[1]));
console.log(payload.role); // "service_role" => leaked admin key
// Server-only usage (Next.js route handler) — no NEXT_PUBLIC_ prefix
export async function POST(req) {
const admin = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE_KEY);
// ...check the caller is allowed first!
}