KeyDrift
Every way a secret ends up in a browser
A client bundle is supposed to contain some credentials. The failures worth losing sleep over are the others: keys that must never reach a browser but do — created at build time, written by an agent chasing a working call, or pasted wherever the demo looked best.
Each page below explains one situation precisely, gives the fix in the same breath as the finding, and finishes with the same offer: fetch your deployment and look.
The failures on this hub share one shape. Nothing throws. The build passes, the deploy succeeds, the smoke test is green, and the key sits in a file that every visitor's browser downloads. By the time anyone thinks to look — usually because an OpenAI bill spikes or a Stripe dashboard shows activity nobody recognises — the string has been public for weeks.
Three forces produce almost every page listed here.
Build-time substitution. Environment prefixes look like configuration and behave like publication. NEXT_PUBLIC_ and VITE_ are instructions to the bundler: take this literal value and write it into the output. The repository never changes; the .env file stays ignored; the leak exists only in the artifact those tools produce. A scanner that reads source reads the wrong file.
Agent imitation. AI coding tools reach for the shortest path that makes the query return rows. Ask for a Supabase call in a Lovable app and the working version holds the service_role JWT, because the SPA has no server to hold anything. Fix one file and the pattern remains in the codebase's gravity; the next feature request reproduces it. This is why regression matters more than discovery — the comeback, not the first appearance, is where monitoring earns its keep.
Credential illiteracy under deadline. An sk_live_ key and a pk_live_ key differ by one letter and by everything. Teams under pressure paste whichever one made the demo work. Part of what KeyDrift does is boring taxonomy: recognising that anon keys, publishable keys and Firebase web keys belong in browsers, so that a service_role JWT beside them gets reported as the emergency it is — and never the reverse.
How to use this section. If you know the tool your app was built with, start in the first group: each page traces one tool's specific mechanics from prompt to served chunk. If you know the build system instead — Next.js, Vite, Nuxt, Remix — the second group explains exactly where substitution happens and what survives it. If you found a string and want to know what it is, the third group covers every provider KeyDrift detects, including the ones that are supposed to be public. And if you already know the worst — a key is live, someone else may have it — skip to the incident group and follow the runbooks in order. Rotation comes before investigation, always.
Two honest boundaries apply everywhere below. These scans see deployed web artifacts only: no mobile binaries, no private networks, no repositories, and nothing behind a login except through paste mode. And detection is pattern plus entropy plus context, not a JavaScript parser — a key assembled at runtime from fragments is outside every bundle scanner's honest reach, ours included.
Secrets left behind by AI coding tools
Lovable, Bolt, Cursor, Claude Code, Replit and friends optimise for “it works” — sometimes the working version holds a live key.
- “Add chat to this page” — how OpenAI keys reach client componentsOne prompt, one client component, one inlined key. Detect the Cursor pattern in seconds and move chat behind a route handler.
- An AI agent left an API key in the browser — generic response runbookTool-agnostic runbook for keys found in client bundles after AI-assisted builds. Rotate, relocate, verify — start with a free scan.
- Bolt.new + Supabase: the service_role default you did not chooseFast scaffolding plus fastest-path wiring usually means one JWT in every bundle. Verify your Bolt-built app free — no signup.
- Bolt.new apps and VITE_ secrets — same trap, fasterEverything in a Bolt preview runs in the browser, including any key you asked it to use. Scan the deployed result free before you trust it.
- Claude Code left an OpenAI key in the browser bundleAgents test what they build; sometimes the working version holds a live key. Runbook: rotate, relocate, re-scan. Free scanner included.
- Copilot and hardcoded keys: autocomplete completes credentials tooCompletions trained on the world’s code include credential-bearing lines. Catch what landed in your bundle — free scan, calibrated findings.
- Cursor wrote my Supabase call into a client componentCursor edits the file you have open; if that file ships to the browser, so does the key it referenced. Scan the deployed bundle free.
- DATABASE_URL in a Replit frontend is the database itself, handed overpostgresql://user:pass@host in a bundle is direct DB access for anyone reading it. Scan your app free — connection strings detected automatically.
- Free security scan for AI-generated web appsFree scan of any deployed web app for exposed API keys — 21 detectors, masked findings, shareable report. No account needed.
- How one insecure pattern spreads across a repo under Claude CodeAgents imitate your codebase — including its worst habit. One client-side key read teaches the convention; find every instance free.
- Lovable app leaking a Supabase service_role key? Find and fix it fastLovable builds client-only Vite apps, so the working Supabase key often ships in the browser bundle. Scan your deployed app free — no account.
- MCP servers and the browser boundary: don’t let tools move credentials client-sideAgent toolchains make it easy to wire providers straight into client code. Audit the deployed surface free — no account needed.
- OpenAI key exposed in a Lovable app — the cost and the cureChat and completion features push an OpenAI key straight into a Lovable bundle. Detect exposure in seconds and move the call behind a function.
- Replit Secrets aren’t private in the frontend — the VITE_ trapReplit’s Secrets panel feels private; add VITE_ and the value is copied into served JavaScript. Free deployed-app scan, no account.
- The five-minute secret audit for vibe-coded appsOne class of failure dominates AI-built apps: secrets in the client bundle. Run the audit free — URL or paste, no account.
- The Lovable/Vite secret-leak mechanism, preciselyVITE_ prefixes tell Vite to inline values into the bundle at build time. Understand static substitution end-to-end, then verify what shipped.
- v0-generated apps and exposed API keys — check before you shipGenerative UI tools place working calls directly in components. Verify the deployed result with a free scan of the live URL.
- Windsurf/Cascade and hardcoded keys — the editor-agent patternEditor agents respond to runtime errors; “invalid key” plus a visible variable is an invitation. Audit the result free — URL or paste.
How builds move secrets into bundles
NEXT_PUBLIC_, VITE_, hydration payloads, source maps, preview deploys: substitution happens after review, alone.
- “But .env isn’t committed” — why Vite apps still leakGitignored is not private: the value never touched git yet reached every browser. The build is the leak. Free scan verifies.
- Astro PUBLIC_ variables and island hydration leaksPUBLIC_ promises to ship. Islands can carry more than you typed if props pass secrets. Verify the deployed output free.
- Cloudflare Pages: public env vars and Functions boundariesVars prefixed public land in the bundle; Workers bindings don’t — until wired client-side. Free deployed-app scan.
- CRA and REACT_APP_: secrets baked into static filesCreate React App inlines REACT_APP_* into HTML/JS at build. Legacy stacks leak too — scan any deployed URL free.
- import.meta.env in production: what Vite actually shipsStatic replacement, not runtime lookup: understand exactly which values survive a Vite build, then verify yours free.
- Netlify deploy previews and exposed environment valuesDeploy previews build with your env vars attached. Check what they ship — free scan of any preview URL.
- NEXT_PUBLIC_STRIPE_SECRET_KEY — the worst possible env nameIf that variable exists anywhere, a payment API key is public. Free scan of your deployment confirms in seconds.
- Next.js + Supabase service_role in a client component — full anatomyThe quickest fix for a build error — adding NEXT_PUBLIC_ — is the one that inlines a bypass-everything key. Scan your Next.js app free.
- Nuxt runtimeConfig: which keys are public by designNuxt streams server data into the page as window.__NUXT__; values read in the wrong context ride along. Free deployed-app scan.
- Published source maps hand back your original code — secrets includedDeployed .map files reconstruct original sources, env reads included. KeyDrift checks maps too — scan free, no account.
- Remix loaders, hydration, and secrets riding alongLoader return values serialize into the document. Return a key once and it ships everywhere. Free scan of any Remix deploy.
- Server Actions that leak: props, closures, and client boundaries’use server’ protects the function body — not what you pass around it. Scan the rendered tree free.
- Static exports and SPAs: there is no server to hide anythingPure front-end deployments cannot hold server secrets, period. Learn the proxy pattern; confirm your build is clean — free.
- Stripe secret key found in a React bundle — impact, rotation, preventionFound sk_live_ in built React output? Rotate now, move checkout server-side. Scanner finds it in seconds — free.
- SvelteKit $env: four imports, one dangerous in the wrong file$env/static/private compiled into a shared module becomes neither static nor private. Learn the boundary; verify your build free.
- Vercel preview deployments are public — and carry your env mistakesPreview URLs ship real env values to anyone with the link. Audit previews like production — free scan, no account.
Credentials by provider
What each key grants, how it travels into a bundle, whether it belongs there, and how to rotate it.
- A leaked Resend key emails as your domainExposed email-provider keys send mail from your verified domains — deliverable, signed, trusted. Scan your app free.
- AKIA keys in JavaScript — pairing is what makes them lethalAKIA strings plus corroborating context is how serious scanners report AWS without false positives. See what ships — free scan.
- An OpenAI admin key in the bundle is org-level compromisesk-admin- can mint keys. Anyone holding yours can replace you on your own account. Treat the whole org as exposed.
- Anthropic keys (sk-ant-) in client bundles — detection and rotationsk-ant- strings in served JS are billable Claude API access. Find them in seconds — free scan, no signup.
- ASIA keys in bundles: temporary doesn’t mean harmlessSTS credentials expire within hours — plenty for automated abuse. Detect alongside permanent keys. Free scan.
- DeepSeek, Groq, Together keys all look like OpenAI’s — exposure is identicalGateways issue OpenAI-shaped keys identifying nothing about issuer. KeyDrift reports honestly as “OpenAI-compatible”. Scan free.
- ghp_/gho_ tokens in bundles — your repositories, read as youClassic PATs usually carry every private repo their owner can see. Scan any deployed site free to rule it out.
- github_pat_ fine-grained tokens — narrower scopes, same urgencyExplicit permissions still include contents:write often enough. Detect github_pat_ strings in served JS — free scan.
- Is your Supabase anon key safe in the browser? Yes — if RLS is realThe anon key belongs in every bundle. What matters is whether policies hold. Decode-and-verify method inside; free scan calibrates.
- Mapbox tokens: pk. is fine — sk.eyJ never isOnly pk. tokens belong in browsers. sk. JWTs can create and delete tokens — account-level access. Free scan distinguishes.
- postgres:// and mongodb+srv:// URLs in client codeScheme-plus-password in a bundle is the shortest possible breach report. Both Postgres/Mongo shapes detected — free scan.
- rk_live_ restricted keys in bundles — scoped, not saferk_ keys carry only granted permissions — which may include charges or refunds. Audit scope after any exposure. Free scan.
- sbp_ tokens in bundles — every project on the account, not just oneA management token is account-level: read, modify, delete every project you own. They should never appear near client code. Free scan.
- SendGrid SG. keys in client JavaScriptThree segments starting SG. — sender access plus your contact lists. Find exposed ones in seconds — free scan.
- Service-account JSON in the bundle — Firebase admin via the browserEmbedded service-account JSON grants administrative access to Firebase/Cloud resources. KeyDrift refines this pattern specifically. Free scan.
- sk_live_ found in frontend JavaScript — severity and responseAny sk_live_ string reachable from the browser is a full account compromise. Rotate now; verify with a free scan.
- sk_test_/pk_test_ in production builds — hygiene with consequencesNo real funds move on leaked test keys — but test customer data becomes readable. KeyDrift reports medium, not critical. Scan free.
- sk-proj- keys in client code — scoped, billable, harvestableScoping limits damage; publication removes it. If sk-proj- appears in served JS, rotate within minutes. Free scan confirms.
- Stripe whsec_ exposed — forged events are the real riskwhsec_ in client code lets anyone POST events your backend accepts as genuine. Find it fast — free scan of any URL.
- Supabase’s new API keys: sb_secret_ must never reach a browserThe newer format makes roles readable at a glance — including by scanners. Verify none of your sb_secret_ shipped. Free scan.
- xoxb- Slack tokens in bundles — someone else posting as your appSlack tokens hide in support widgets and notification demos. Detect xox-prefixed strings in served JS — free scan.
- Your Firebase web API key is supposed to be publicGoogle publishes one in their own docs. Calm down, then verify restrictions. Calibration page — no rotation theater.
Found a leaked key
Runbooks in response order: rotate first, audit usage, fix the source, verify the deploy.
- A Stripe secret key leaked: the payments-specific runbookCharge audits, refund review, webhook rotation, restricted-key replacement — Stripe-specific order of operations.
- After the incident: make sure it cannot come back quietlyDrift monitoring watches the reintroduction pattern specifically. Regression alerts name it explicitly. Free tier starts.
- AWS credentials leaked: the IAM remediation sequenceCreate-before-suspend ordering, CloudTrail triage checklist, GuardDuty activation — AWS-specific response.
- Do you have to tell anyone about a leaked key? Considerations, not advice.Neutral factors inventory for disclosure decisions — questions for counsel, framed calmly. Non-advisory by design.
- Found an API key in your bundle? The first 60 minutes, in order.Rotate first, audit usage second, fix source third, verify deploy fourth. Step-by-step runbook — free scan to start.
- Revoking an exposed OpenAI key (and what to check first)Usage evidence, revocation path, project-scoped replacement, spend caps — the OpenAI-specific incident flow.
- Rotating a Supabase service_role key without breaking productionDashboard path, env order, edge-function redeploy, client verification — the full sequence. Free scan confirms removal.
- Someone has a GitHub token that opens your repositoriesRevoke, audit the security log, then assume every secret those repos hold moved too. Second-order exposure explained.
- Was my exposed key actually used? How to find out.Per-provider audit paths confirming or ruling out abuse — then rotate anyway. Epistemics handled honestly.
- Your key is in git history. Does purging matter more than rotating?Rotation makes history inert; rewriting makes it tidy. Do them in that order. Tools credited by category, calmly.
Straight answers
Symptom-first pages for the questions people actually type at an assistant.
- Are environment variables safe in frontend code? Only some ever are.Prefix taxonomy across NEXT_PUBLIC_/VITE_/REACT_APP_/PUBLIC_ in one passage; manual verification steps; free checker.
- Did your AI assistant leak a key? A five-minute self-check.Three triage questions mapped to signature strings; DevTools walkthrough; automated confirmation. Calibrated reassurance included.
- How attackers actually find exposed API keysBulk retrieval, not intrusion: crawl CDNs, regex extraction, validation calls, resale/drain. Defense inversion included.
- Is my API key public? Check any deployed site in seconds.Fetch what the public fetches and look — free checker covering 21 detectors across 13 providers. Masked, shareable reports.
- Strange refunds or charges on your Stripe account?Legitimate causes ranked first, leak indicators second, audit paths third — then a free scan closes the hole.
- Why is your OpenAI bill spiking? One cause hides in your JavaScript.Sudden unexplained spend often means an exposed key being scraped. Check deployed bundles free — results in seconds.
Run one check now
Every page on this hub ends the same way, because there is only one honest way to know what a deployment serves: fetch it and look. The scan is free, needs no account, and takes about as long as reading this sentence.
KeyDrift · free URL or paste-source scan