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.

By

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

Where it shows up

Related signals

Checking whether your own page does this takes one paste, not a manual audit.
Scan a page for this signal →