cd /news/ai-tools/i-scanned-978-vibe-coded-apps-1-in-2… · home › topics › ai-tools › article
[ARTICLE · art-156071] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=↓ negative

I scanned 978 vibe-coded apps. 1 in 26 ships a secret API key to every visitor.

A developer built LeakLock, an open-source MIT-licensed scanner that inspects what a deployed site actually serves — HTML, JS bundles, lazy-loaded chunks and source maps — rather than the git repository. Scanning 978 public apps built with tools like Lovable, Bolt and v0, the developer found that roughly 1 in 26 ships a secret API key to every visitor, often via VITE_, NEXT_PUBLIC_ or REACT_APP_ environment variables baked into client bundles. The tool traces how a string is used — as an Authorization: Bearer header, an x-api-key header or a ?key= parameter — to identify credentials regardless of format, while marking intentionally public keys such as Supabase anon keys and Stripe pk_ keys as safe.

by read2 min views1 publishedOct 23, 2026

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?

── more in #ai-tools 4 stories · sorted by recency
── more on @leaklock 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/i-scanned-978-vibe-c…] indexed:0 read:2min 2026-10-23 · —