Detect a service_role key in your client bundle
The service_role key carries BYPASSRLS. Every policy you have written is decorative to anyone holding it. Once it is in a bundle served to browsers it is public, and no amount of RLS work matters until it is rotated.
This happens most often when an AI tool needs an admin operation, finds the service key in the environment, and wires it into client code because that is where the call was being written.
Rules that check this
- criticalservice_role key shipped to the browser
SERVICE_ROLE_KEY_EXPOSED
How the check works
VibeGuard fetches your deployed page, follows the same-origin script tags and module preloads, and scans the bundles for Supabase credential formats: legacy JWTs, `sb_secret_*`, `sb_publishable_*` and management tokens `sbp_*`.
A JWT is classified by decoding its payload and reading the `role` claim — unverified, deliberately, because the question is what the key claims to be, not whether the signature is good.
The key is never stored
A finding records a fingerprint — the first six characters and a length — never the key. Recording a live RLS-bypassing credential in our database, then rendering it in a dashboard, would make the leak worse.
What to do when you find one
Rotate it first, in the Supabase dashboard under Settings → API. Everything using the old key stops working, which is the point: you need that list.
Then move whatever needed it behind a server route, a Next.js Route Handler, or an edge function. If the operation genuinely needs to bypass RLS, it does not belong in code you ship to browsers.
Frequently asked
- Do you need access to my repository?
- No. The check reads your deployed site the way any visitor can. No GitHub access and no signup.
- What if my config loads at runtime?
- Then the bundle scan finds nothing and the report says so. Paste your project URL and anon key directly and the table checks still run.
- Is the scan safe to run against production?
- It only issues GET, HEAD and OPTIONS. There is no code path that can write, and loopback and private address ranges are refused before the socket opens.
Check your project in about ten seconds
Paste a URL. No signup, no writes, nothing stored.
Run the free audit