The secrets hiding in your JavaScript

GitHub, Slack, OpenAI: which tokens end up in frontend code, and what they unlock

Not every secret you leak is a payment key. Most of the credentials that end up in frontend bundles are the boring operational ones: a token for a deploy script, a Slack bot for notifications, an OpenAI key for a feature you shipped last month. They feel low-stakes. They aren't.

So which of them made it into the JavaScript you serve, and do you know what each one opens? Those two questions decide how bad a leak gets, and most teams can't answer either off the top of their head.

A leaked ghp_ token is read access to your private repos, which is your entire source code, including every other secret hardcoded in it. That's the one people underestimate the most, so start there.

The prefixes that give them away

These tokens are easy to spot because they announce themselves. Each provider stamps a fixed prefix on the front, partly so their own scanners can catch leaks. That same prefix is what an attacker greps your bundle for.

ghp_GitHub personal access token
Read (often write) every private repo on the account. Clone your source, push commits, read Actions secrets.
xoxb- / xoxp-Slack token
Read channels and DMs, post as the app or the user, pull your member directory.
sk-OpenAI / Anthropic key
Bill your account to its limit. A cheap key to steal, an expensive one to own.
GOCSPX-Google OAuth client secret
Impersonate your app in the OAuth flow, mint tokens, hijack the consent screen.
The prefix is the tell. A scanner matches it in milliseconds across your whole bundle.

A ghp_ token is a GitHub personal access token. Depending on its scopes it reads, and often writes, every private repository on the account. An attacker who finds one clones your source, reads it for more secrets (database URLs, other API keys, signing secrets, the lot), and can push commits or read your Actions secrets. Source code access is rarely where the attack stops. It hands them everything they need to plan the next step.

A Slack token (xoxb- for a bot, xoxp- for a user) reads channels and direct messages, posts as the app, and pulls your member directory. An OpenAI or Anthropic key (sk-) bills your account until it hits the limit, which is cheap to steal and expensive to own. A Google OAuth client secret (GOCSPX-) lets someone impersonate your app in the OAuth flow. And these are just the named ones. Slack, GitHub, and Google all publish format docs precisely so leaked tokens can be detected by pattern, which cuts both ways.

How they get into the bundle

The mechanism is the same one that ships a Stripe secret key or an AWS key. A build-time public prefix, NEXT_PUBLIC_ in Next.js or VITE_ in Vite, inlines whatever you name with it straight into the client bundle. So NEXT_PUBLIC_GITHUB_TOKEN gets shipped to every visitor, on purpose, with no warning.

The other path is a feature that calls an API directly from the browser. You add a "send to Slack" button and put the bot token in the component because the server route felt like overkill. You build a chat feature and call OpenAI from the client to skip a hop. Each one drops a real credential into the bundle. The code works, the demo looks great, and the token is now public.

Tokens also hide in places people don't search. A committed .env.local that got bundled, a config object printed to the console during debugging, an old integration left in after you rewrote the feature. The token outlives the code path that used it.

yoursite.com
SourcesConsoleNetwork
searchghp_
app.4f1ed1.min.js
!function(e){var t={};function n(r){...e.exports}return n(0)}
,a.key="ghp_A1b2C3••••wXyZ",
t.charge=function(e){return r.post("/v1/charges",e)}
1 match - your live secret key is in the bundle.
A GitHub token in the bundle. To an attacker's crawler it's a string match, then a login.

What a stranger does with it

Finding the token is the easy half and they barely do any work. Automated crawlers scan public bundles for these exact prefixes all day, then test each hit against the provider's API to see if it's live and what it can reach. A human never has to look at your site.

Once a token validates, there's nothing left to defeat. No password, no second factor. The token is the access. A ghp_ hit gets cloned and mined for more secrets within minutes. An sk- hit gets plugged into someone else's app to run inference on your bill. An xoxb- hit reads whatever your bot could read, which in a small team is often everything.

The provider doesn't alarm on this, because to the provider it's a valid token making valid calls. GitHub's secret scanning will sometimes catch a token committed to a public repo and auto-revoke it, which is genuinely helpful, but a token minified into a deployed JavaScript bundle isn't in your repo. It's on your CDN. That safety net doesn't reach it.

Check your bundle, then rotate what you find

You can do this now, no tools required.

  1. Open your site and open developer tools (F12).
  2. In the Sources tab, search across all loaded files (Ctrl+Shift+F, or Cmd+Option+F on a Mac).
  3. Search for each prefix in turn: ghp_, github_pat_, xox, sk-, GOCSPX-.

Any match is a public credential. Rotate it at the provider first, before you redeploy, because the leaked token stays valid as long as the old bundle is cached. Cleaning the code without revoking the token leaves the door open. Then move the call that needed the token to a server route, so the browser asks your backend and your backend holds the secret. The rule that sorts this out is the same one in public versus secret keys: if a string grants action and isn't meant to be seen, it doesn't belong in the browser.

We scan your bundle from outside for these prefixes, the same way the crawlers do, and tell you which ones are sitting in your shipped JavaScript. We can't tell you a token's scopes or what it reached after it leaked, because that's on the provider's side, not ours. We see the leak. Revoking and auditing is your move, and it's the one that matters most.

Find it before someone else does.

Paste your domain. The grade and issue count are free, and you'll see in a couple of minutes exactly what's reachable from outside.