Checklist
Leaked a GitHub token? What to do in the next 15 minutes (and how to stop it happening)
Short answer. Treat the token as already copied. Revoke it on GitHub, read the security log, look for unexpected Actions branches, rotate every secret that token could reach, and check whether a repository was deleted. A fine-grained token on one repo, with a short expiry and no admin permission, is the replacement.
Updated 28 Oct 2026
This is a practical checklist, not a guarantee. It is not a penetration test, a compliance audit, or proof that an app is safe to launch.
A GitHub token is a password for the API. Whoever has it can do whatever that token was allowed to do, on every repository it can reach. Deleting it from a chat or a .env file does not revoke it. This page is the first sitting after a leak, and the habits that make the next one smaller. It is a checklist, not a statement that the account is fine.
What was reported this week
Three public reports from October 2026 are why this is worth doing today. They are separate incidents. None of them is a verdict on your account.
A private repository, one CLI delete. On 28 Oct 2026, a write-up of a public report described a company co-founder saying that an account with no membership, invite, or role in their GitHub organization deleted one private repository. The audit log they shared showed a single repo.destroy from the GitHub CLI, using an OAuth token whose scopes included delete_repo, on 27 Oct 2026. The owner said the repository was not in the deleted-repositories list. The write-up says it has not seen GitHub's side, and it lists a leaked or shared token as one unconfirmed explanation, next to outside-collaborator access and other guesses. It is not a confirmed product flaw. What the log does show is enough for this checklist: a token with delete_repo can delete repositories that token's owner is allowed to administer. GitHub's `gh repo delete` manual says that scope is not part of a normal gh login. You add it with gh auth refresh -s delete_repo.
Stolen tokens, secrets in Actions logs. On 25 Oct 2026, Wiz Research described Gheist, a campaign it has tracked since 15 Sep 2026. Stolen GitHub tokens are used to push a branch named ci-sync- plus six characters. Wiz's indicator is ci-sync- followed by six letters or digits, ^ci-sync-[0-9a-z]{6}$. The only new file is .github/workflows/sync-check.yml, with the commit message ci: sync config. The workflow prints the repository's secrets into its own log in a form GitHub's verbatim log masking does not catch. The tool then downloads that log through the GitHub API and deletes the run seconds later. API calls use the user agent gheist. Wiz said it had recovered more than 13,500 planted branches from more than 850 compromised accounts, including private repositories, and that as of 24 Oct 2026 the campaign was still ongoing. A GitHub hold on suspicious workflow runs in public repositories, in place since 28 Jul 2026, left the public runs Wiz checked waiting for approval. Wiz says that hold does not apply to private repositories or GitHub Enterprise Server, and that runs there completed and were then deleted. Deleting the branch does not undo a log that was already downloaded.
A planted instruction file in Downloads. On 22 Oct 2026, Forever Security reported a malicious AGENTS.md in a customer's ~/Downloads folder. The file told OpenAI Codex to register an MCP server that installed function-color, a malicious npm package Forever says npm has since removed. In Forever's tests of that file, Codex carried out the install in more than 90 percent of attempts. Forever also wrote a CLAUDE.md that pointed Claude Code's API traffic at a server Forever controlled, and said Claude Code followed it about 50 percent of the time, sending an OAuth token to that server. The agents were doing what they are built to do: read the instruction file in the folder where they were started.
The next 15 minutes
Do these in order. Stop and rotate before you clean up git history. GitHub's leaked-secret tutorial says that removing the secret from the latest commit, or deleting the repository, does not by itself stop a value that was already copied.
Revoke the token on GitHub. A personal access token is deleted under Settings, Developer settings, Personal access tokens. Classic tokens are listed at github.com/settings/tokens. Fine-grained tokens are on the Fine-grained tokens page next to that. GitHub's token docs say to delete a token that is no longer needed. If the leak was a gh auth login session, that is an OAuth token, which is the kind of token in the 27 Oct log. GitHub's authorized OAuth apps list is Settings, then Integrations, then Applications, then the Authorized OAuth Apps tab. Revoke the GitHub CLI entry there, and run gh auth logout on the machine that stored it. Revoke every token you cannot account for, not only the one you remember pasting.
Read the security log. A personal account's security log lists actions from the last 90 days. GitHub's security log docs say you can filter by action, actor, and repository. Look for repo.destroy, new public keys, and OAuth or personal-access-token authorizations you do not recognize. If the repository belongs to an organization, also open that organization's audit log. A quiet personal log does not cover an organization you do not own.
Review Actions, branches, and workflows. On each repository the token could push to, open the branch list and the Actions tab. A Gheist branch looks like ci-sync- plus six characters, and adds only .github/workflows/sync-check.yml with the message ci: sync config. Also look for workflow runs on those branches that were deleted within a minute, and for the user agent gheist in the audit log. Do not approve a held sync-check run. If a run already completed, Wiz's response steps are to rotate every repository, organization, and environment secret that repository could read, and to revoke the token that pushed the commit. A missing run is not evidence the log was never downloaded.
Rotate every secret the token could reach. A token that can push a workflow can cause Actions to print secrets into a log, which is the Gheist path. Rotate repository secrets, environment secrets, and organization secrets shared with that repository. Then rotate anything else that lived in those repos or in the same chat: cloud keys, database URLs, and the keys in API keys leaking into the frontend. If the workflow ran on a self-hosted runner, Wiz says to treat that host as compromised. GitHub-hosted runners do not leave you a machine to rebuild.
Check for a deleted repository, and the restore window. For a personal account, open Settings, Repositories, Deleted repositories. For an organization, the same list is under the organization's Settings. GitHub's restore docs say some deleted repositories can be restored within 90 days, and that it can take up to an hour after deletion before the repository appears. A repository that was part of a fork network that is not empty cannot be restored that way. GitHub also documents that deleting a private repository deletes its private forks. If the list is empty and you still had a local clone, that clone is the copy you can push back up. The 27 Oct report said the repository was missing from this list. That is a reason to look, not a reason to skip the revoke.
Stop the next one
GitHub's token docs say a classic personal access token can access every repository you can access, and they recommend a fine-grained token when it can do the job. Use these limits:
- One fine-grained token per job, limited to one repository, with only the permissions that job needs. Do not grant repository Administration. GitHub's delete-repository API says deletion requires admin access, and a classic or OAuth token also needs the
delete_reposcope. - A short expiry. The fine-grained creation form defaults to 30 days when you do not pick one. GitHub allows 1 to 366 days, or no expiry, and says an organization can impose a shorter maximum. Prefer the short end. GitHub also removes classic tokens that have not been used for a year. That is not a substitute for an expiry you chose.
- No
delete_repoon a token an agent or a laptop keeps. If you must delete a repository, add the scope for that command and remove the authorization when the command is done. - Secret scanning and push protection, with the limits GitHub actually documents. Secret scanning runs automatically, for free, on public repositories. On organization-owned private repositories it requires GitHub Secret Protection on GitHub Team or GitHub Enterprise Cloud. A typical user-owned private repository is not in that list. Push protection for users is on by default and blocks pushes of detected secrets to public repositories. It does not cover private repositories. Push protection on a specific repository requires GitHub Secret Protection and is off until someone turns it on. Turn that on where your plan includes it. Do not assume a private hobby repo already has it.
- Never paste a token into an AI chat, and do not commit
.env. The chat transcript is another copy. The longer note is How to keep API keys out of AI-generated code. - Keep a copy that is not only on GitHub. A clone on a disk you control is the restore path when the 90-day list is empty or the repository was in a fork network. GitHub's window is not that copy.
GitHub Advanced Security is the directory entry for GitHub's own scanning. What you get still depends on the plan and on whether the repository is public. Semgrep is a scanner you run on a git repo, including secrets rules, which is useful when GitHub's private-repo scanning is not on your plan. Neither tool revokes a token. You still do that in GitHub's settings.
If an agent is in the loop
Forever's report is about the folder the agent starts in. AGENTS.md and CLAUDE.md are instructions the agent reads from that folder. ~/Downloads is a folder a browser can write. Starting the agent there is how the planted file got read.
- Start the agent inside the project directory. Do not start Codex, Claude Code, or another coding agent in
~/Downloadsor in your home directory. - Read
AGENTS.md,CLAUDE.md, and changes to MCP or agent config before you approve them. That includes a repository you just cloned. The file Forever found was a long engineering note. The install instruction was one section, not the title. - Leave approval prompts and the sandbox on. Refuse a step that adds an MCP server you did not ask for, changes an API base URL, or asks the agent to bypass approvals.
- If you find a planted file, Forever's advice is to assume the agent's login token leaked and rotate it. Check
~/.codex/config.tomlfor an MCP server you did not add, and a project's.claude/settings.jsonfor an API address you did not set.function-colorin an install list is the package Forever named.
An agent that can run gh uses the login already on that machine. A delete_repo scope left over from a cleanup is a delete button for every later session.
Where this fits
The same leak in a browser bundle is API keys leaking into the frontend. The pass over the whole app before you share a URL is the pre-launch security checklist.
Run a free passive check on the public URL of the app. It looks at pages a browser would load. It does not read your GitHub account, a private repository, or a token that never shipped to the site. Revoke the token even when that check is quiet.
FAQ
I deleted the token from my laptop. Is that the revoke?
No. GitHub's token docs tell you to delete the token in Developer settings. A copy in a chat, a CI secret, a shell history, or another computer keeps working until GitHub has revoked it. Removing the line from .env is the cleanup after the revoke, not the revoke.
Will secret scanning alert me about a private hobby repository?
Not by the rule GitHub publishes. Secret scanning is automatic on public repositories. Organization-owned private repositories need GitHub Secret Protection. User-owned private repositories are not in the availability list, except the enterprise cases GitHub names. Push protection for your user, which is on by default, blocks detected secrets on pushes to public repositories only.
The Actions run is already gone. Do I still rotate?
Yes, if the branch or the workflow was there. Wiz says the campaign downloads the log and deletes the run seconds later. A deleted run is part of that sequence, not a sign that the secrets stayed in GitHub. Rotate the secrets the repository could read, and revoke the token that could push.
Can the free URL check find a leaked GitHub token?
Only if that token is in a public page or a public script the check fetches. It does not sign in to GitHub, and it does not scan a private repository. Use it for the deployed app. Use this page for the token.
Sources
Checked 28 Oct 2026.
- Wiz, Gheist: attempted scaled abuse of stolen GitHub tokens
- Forever Security, a planted AGENTS.md in Downloads
- Write-up of the 27 Oct 2026 repo.destroy report
- GitHub, managing personal access tokens
- GitHub, reviewing your authorized OAuth apps
- GitHub, reviewing your security log
- GitHub, remediating a leaked secret
- GitHub CLI, gh repo delete
- GitHub, restoring a deleted repository
- GitHub, forks when a repository is deleted
- GitHub, delete a repository (REST)
- GitHub, about secret scanning
- GitHub, about push protection
Informational only. This page describes checks and vendor documentation. It is not a security audit, a penetration test, or a certification, and it is not a guarantee.
Newsletter
A short weekly note on new tools and security checklists. No spam, unsubscribe anytime.