A few weeks ago I found an image-generation API key sitting in my own app's JavaScript bundle. Anyone who opened DevTools could copy it and bill me.
The confusing part: Gitleaks said my repo was clean. And it was. The key never touched git. It came in through a VITE_ environment variable I'd set in my hosting dashboard, and the build baked it straight into the bundle.
That's the gap. Secret scanners check your repo. Nobody checks what your site actually serves.
I collected 978 public apps built with tools like Lovable, Bolt and v0 (all linked from open GitHub repos) and scanned what each one sends to a browser: the HTML, the JS bundles, lazy-loaded chunks, and source maps.
What came back:
No sites are named here, and keys were masked during the scan. The point isn't to shame anyone. Every one of these is an honest mistake that the tooling makes very easy.
VITE_, NEXT_PUBLIC_ or REACT_APP_ goes into the client bundle. That's by design, but it's easy to forget when an AI assistant writes import.meta.env.VITE_OPENAI_KEY for you.
Regex works for sk-... or ghp_.... It doesn't work for a random 40-character string that happens to be a key.
So instead of only matching shapes, I look at how the code uses a value. A string sent as Authorization: Bearer, in an x-api-key header, or as ?key= in a request is almost certainly a credential, whatever it looks like. Trace the minified variable back to its literal, read the request host, and you can name the API too.
The opposite problem matters just as much. Supabase anon keys, Firebase web config and Stripe pk_ keys are meant to be public. Flagging them is noise, so they're marked safe.
I packaged this as LeakLock: open source, MIT, zero dependencies.
npx github:sgroy10/leaklock scan https://your-site.com
Or in CI, scanning your deployed preview on every push:
- uses: sgroy10/leaklock@v1
with:
target: https://your-preview-url.com
fail-on: secret
Repo: https://github.com/sgroy10/Leaklock
Spend two minutes on this:
key, token and secret.
What's the worst key you've found in a client bundle, yours or someone else's?