Your GitHub token leaks the second you push. Here is how it happens, why scope makes it ruinous, and what to actually check tonight.

It is 11pm. You ship a tiny script that posts to your GitHub repo from a cron job. Cursor wrote most of it. You drop your personal access token into .env.local, run it locally, it works, you commit. You did not notice that .env.local was not in .gitignore on this side project. You pushed. By 11:01pm, three scraper bots have your token. By 11:30pm, one of them has pushed a new file at .github/workflows/build.yml that runs a Monero miner on GitHub Actions. By morning, your private repos have been cloned, a deploy key has been added to one of them, and your Vercel preview is still using the same token because you never redeployed after rotating it. This happens to solo founders every day, and the part that hurts is that the token did not need to exist at all.

02Your token is public the second you push

The mental model most founders carry is that pushing to a private repo means the token inside is private. It is not. There is no such thing as a 'private commit' the moment your token hits any remote that another human or service ever touches. Scraper services watch the GitHub public events firehose in real time. The window between push and exploit is measured in seconds, not hours. Here is how it usually ships from a Cursor session. You ask for a script that uses the GitHub API. Cursor writes `const token = process.env.GITHUB_TOKEN`. You create a .env file. You forget that your .gitignore on this fresh repo only excluded node_modules. You commit, you push, the token is in the diff forever. Even if you delete it in the next commit, git history keeps it. Run this check tonight in every repo you own: `git log -p --all -S 'ghp_' | head` `git log -p --all -S 'github_pat_' | head` `git log -p --all -S 'gho_' | head` If anything comes back, that token is compromised. Revoke it at github.com/settings/tokens before you finish reading this article. Rewriting history with git-filter-repo does not undo the seconds the token was visible.

03Scope is the part that bites

A leaked token is a key. The blast radius depends on what door it opens. Most founders click 'select all scopes' on a classic PAT because the GitHub UI does not explain what any of them actually do, or they generate a fine-grained PAT with 'all repositories' because choosing repos one by one is annoying. The two scopes that ruin your week are `repo` and `workflow`. The `repo` scope on a classic PAT is read and write on every private repo the user can see. If you are in an organization, that includes work code, side projects, and anything a collaborator shared with you. The `workflow` scope lets an attacker write a new file under .github/workflows. The moment that file is pushed, it runs on a GitHub hosted runner with access to every secret you stored in Actions. That includes your STRIPE_SECRET_KEY, your SUPABASE_SERVICE_ROLE_KEY, your production database URL, all of it. Open github.com/settings/tokens right now. For every token in that list, ask one question: if this token leaks tonight, what does the attacker get? If the answer is anything more than 'one specific repo, read only,' the token is overscoped. Fine-grained PATs scoped to one repository with the minimum permissions are the only kind worth creating in 2026. Anything else is convenience you are paying for in attack surface.

04Rotation is not the fix

You revoked the token. You feel relieved. You should not. Revocation cuts the attacker off from new actions, it does not undo the actions they already took. In a fifteen minute window, a token with `repo` and `workflow` scope opens a lot of doors. Walk this checklist before you close the tab. Visit github.com/settings/security-log and filter the last seven days. Look for things you did not do: new SSH keys, new deploy keys on any repo, new GitHub App installations, new personal access tokens, new collaborators, new workflow files, new forks. Then open each repo and check the Actions tab for runs you do not recognize. Check Settings for deploy keys and webhooks you did not add. An attacker who knows what they are doing leaves a deploy key, not a new token, because deploy keys do not show up in your tokens page. Then check your live deploys. If your Vercel project had the token as an env var, you have to redeploy for the new value to actually take effect. Preview deployments cache the old value too. Same for Railway, Render, Fly, anywhere the secret was baked into a build. Guardian scans your repo history for committed GitHub tokens, checks the scope on every active PAT, and flags the deploy keys, workflows, and webhooks an attacker would leave behind. Plug in your GitHub and find the token you forgot you committed before a scraper does.

The Guardian Team
Security for apps built with AI.

Find the GitHub token you forgot you committed

Guardian scans your repo history and live env vars for leaked PATs, checks the scope, and flags the deploy keys and workflows an attacker leaves behind.

Scan my app free
More articles