GitHub token in the browser bundle after a Claude Code session
Repository access under the token’s scopes — for a classic PAT, usually every private repo the user can see.
Why Claude Code does this
A CLI agent working across the repo will follow the pattern it already sees. If one component reads a key from `import.meta.env`, the next feature it writes will too — the leak spreads by imitation rather than by a single mistake.
Confirm it first
Before rotating anything, check whether the key is actually being served. Paste your deployed URL — KeyDrift downloads the same JavaScript a visitor gets and tells you what is in it.
Rotate the key
Do this before changing any code. The key has been served to browsers, cached by CDNs and very likely scraped already — removing it from the source does not un-publish it.
- 1Revoke the token in Settings → Developer settings → Personal access tokens.
- 2Check the account’s security log for pushes, clones and workflow runs you did not make.
- 3Re-issue as a fine-grained token scoped to the one repository that needs it.
Move the call to a server
The replacement key must not follow the old one into the bundle, which means the code that uses it cannot live in the browser.
Before — shipped to the browser
// src/components/Chat.tsx
// Vite substitutes the literal value here at build time.
const key = import.meta.env.VITE_API_KEY;
const result = await callTheApi(key, body);After — stays on a route handler or server action
// supabase/functions/proxy/index.ts — runs on a route handler or server action
Deno.serve(async (request) => {
const key = Deno.env.get('API_KEY')!; // never sent to the browser
const result = await callTheApi(key, body);
return Response.json(result);
});
// src/components/Chat.tsx
const result = await fetch('/functions/v1/proxy', { method: 'POST' }).then((r) => r.json());How KeyDrift detects it
Matches the `ghp_`, `gho_`, `ghu_`, `ghs_` and `ghr_` prefixes. A classic personal access token usually carries every private repository the user can see.
It will happen again
A CLI agent working across the repo will follow the pattern it already sees. That has not changed because you fixed this one file — the next feature request produces the same shape of code. Continuous monitoring re-scans every deploy and tells you the moment a key comes back.