Blog Source and build
The credentials sitting in your page source
Thirty points and a hard score floor, because it's the one finding here that costs real money. What a single HTML fetch can prove about leaked keys, why Firebase's API key is deliberately not flagged, and what the Lovable CVE actually taught everyone.

Most of the detectors on this site are opinions about taste. This one isn't. Secrets in the client bundle is worth 30 points and carries a score floor of 62, meaning no amount of craft elsewhere on the page can argue the number back down. It's the only finding here where the correct response isn't to redesign anything. It's to rotate the key today.
What it's actually looking for
Eight patterns, all of them shapes that only ever appear on real credentials: OpenAI-style sk- keys, Stripe live secret keys in sk_live_ or rk_live_ form, AWS access key IDs starting AKIA, GitHub tokens in the ghp_ gho_ ghu_ ghs_ ghr_ family, Slack xox tokens, anything naming a Supabase service_role key, PEM private key blocks, and Google API keys.
None of these are guesses about entropy or heuristics about variable names. They're the literal formats these providers issue, which is why this detector can carry a floor when almost nothing else does. A page either contains a string shaped exactly like a live Stripe secret or it doesn't.
The exception that explains the rule
Google API keys start with AIza, and Firebase web configuration includes one. That key ships to the browser in every Firebase app on the internet, on purpose, by design. So the detector deliberately skips any AIza key that has the word firebase within the next 200 characters.
This matters more than a parsing detail, because it's the same misunderstanding underneath most of the panic in this area. A Firebase web key is an identifier, not a password. It tells Google which project a request belongs to. It does not grant access. The thing standing between a stranger and your data is your security rules, and a key that ships publicly is not evidence those rules are wrong.
Flagging a Firebase key as a leaked secret would be technically detectable and completely wrong, which is a bad combination for a tool people are meant to trust.
How common this actually is
Uncomfortably. A 2025 scan by the security firm Intruder across roughly five million applications turned up more than 42,000 tokens sitting in JavaScript bundles, ranging from source control tokens to project management API keys. Separately, well over six million keys are leaked to public GitHub repositories every year. This is not an exotic failure mode. It's one of the most common ones there is.
The database signal, and why it's only worth 6
There's a second, much lighter detector that fires when a page talks to Supabase or Firebase directly from the browser. It's worth 6 points, one of the lightest weights in the whole set, and that restraint is the entire design.
Addressing your database from the client is a completely legitimate architecture. Stripe's own homepage trips this signal. So does tailwindcss.com. What it means is that row-level security or Firebase rules are now the only thing between a visitor and your tables, and one server-side fetch of one HTML document has no way whatsoever to know whether those rules are switched on. So the detector says go and check. It does not say caught.
Why that distinction stopped being theoretical
In January 2025, security researcher Matt Palmer scanned 1,645 applications built with Lovable and found 170 of them, just over 10 percent, with exposed databases across 303 vulnerable API endpoints. The projects were being generated with Supabase databases where row-level security either wasn't enabled or didn't match the app's actual logic. After a 45-day disclosure window closed without a meaningful fix, he published the findings as CVE-2025-48757 in May 2025.
The mechanism is worth understanding precisely, because it's easy to draw the wrong lesson. The Supabase anon key embedded in those pages is public by design, exactly like the Firebase key above. Publishing it was not the vulnerability. The vulnerability was that with row-level security missing, that public key was sufficient to read entire tables: user lists, payment records, stored tokens. The key was never the problem. The key was the thing that made the missing policy exploitable.
That's exactly why the detector here is worth 6 points and phrased as a prompt to check rather than an accusation. Ten percent of a large sample being exposed is a serious number. It's also not a hundred percent, and a scanner that can't see your database has no business claiming to know which group you're in.
What to do, in order
- If a real secret is in your page source, rotate it now, before fixing the code that put it there. Assume it's compromised; it was served to every visitor and quite possibly cached and crawled.
- Move the call server-side. An API route or edge function that holds the key and proxies the request is the whole fix, and it's usually an afternoon.
- Learn which of your public-looking keys are genuinely public. Firebase web config and the Supabase anon key are meant to ship. A service_role key never is, and it's the one people paste in when the anon key doesn't work.
- Test your row-level security the way an attacker would: query your database with only the anon key and no authentication. If you can read something you shouldn't, the policy is wrong. Enabling RLS is one statement per table.
- Turn off source maps in production, or at least know that you're shipping them. It's a light 4-point signal here, but it hands anyone reading your bundle the unminified original.
Checking whether your own page does this takes one paste, not a manual audit.
Scan a page for this signal →