GitHub News

GitHub now blocks pushes containing Anthropic keys by default. Here's what your agent will hit

Push protection covers two new Anthropic key types from 13 October. The block is easy to trip when an agent commits in a loop, and the obvious fix, a follow-up commit that deletes the key, does not clear it.

A concrete gatehouse with a heavy arched door slightly open, a thin line of violet light crossing the floor

GitHub’s secret scanning now recognises two more Anthropic key types, and push protection blocks them by default. The 13 October changelog entry names anthropic_service_api_key and anthropic_user_api_key, and says repositories with secret scanning enabled, free public ones included, now reject a push that contains either. The same entry adds detectors for Cloudsmith keys and a Salesforce Marketing Cloud OAuth2 token.

If you run coding agents, that changes what a bad afternoon looks like. Before, an agent that wrote a key into a config file and committed it left a secret in your history that you found later, or never. Now the push fails, at the point where the agent is most likely to be unattended and most likely to improvise a fix.

What was added

GitHub’s supported patterns page lists five Anthropic token types: admin API key, API key, service account API key, session ID and user API key. All five are partner patterns with user alerts and push protection. Partner means that when a match turns up in a public repository, GitHub forwards it to Anthropic so the key can be revoked. Anthropic has been a secret scanning partner since 2024, so detection isn’t new. The change is that the two newest types are blocked at push time without anyone switching anything on.

Where it applies matters. Push protection for users is on by default for public repositories on GitHub.com and attaches to your personal account. Push protection for repositories, the version admins configure with bypass controls, is off by default and needs GitHub Secret Protection. The docs say it covers command-line pushes, commits in the web UI, file uploads, REST API requests and, for public repositories, GitHub MCP server interactions. The changelog entry doesn’t say how to turn the new default off, and the docs pages above don’t either, so assume you can’t.

What the agent sees

A blocked command-line push prints remote: lines naming the secret type, then each commit, file path and line number that contains it, plus a URL for reviewing the block. That’s the whole interface. An agent reading those lines gets enough to find the file. It doesn’t necessarily get the part that matters, which is that every commit in the push is scanned, not just the tip.

REST callers get a different failure. Per the docs, the Create a blob and Create or update file contents endpoints return a 409 when they find a supported secret. An agent using the GitHub MCP server against a public repository goes through the same check, so it hits the same wall.

The fix that doesn’t work

The natural agent response to a rejected push is to delete the key and commit again. The docs are explicit that the secret has to be removed from every commit that contains it. A later commit that deletes the line leaves the earlier commit as it was, and the earlier commit is part of the push.

Here’s the situation in a throwaway repository, with a dummy value standing in for the key. Three commits: an app, a config file that includes the key, and a README. The key line is then deleted and folded into the last commit with --amend:

sed -i '/ANTHROPIC/d' .env && git add .env && git commit -q --amend --no-edit
git log --oneline
git log -p --all -S"not-a-real-key" --oneline | grep -E "^[0-9a-f]{7} |^\+ANTHROPIC"

Run it and it prints:

356cd94 docs
58e0350 add config
9f3f6ee app
356cd94 docs
58e0350 add config
+ANTHROPIC_API_KEY=not-a-real-key

The first three lines are the history. The last three are the search: 58e0350 still adds the key, and 356cd94 is the commit that removes it. Both are in what would be pushed. The amend fixed the working tree and the tip, and did nothing for the commit in the middle.

The docs give the interactive route for this: find the earliest commit containing the secret, run git rebase -i <COMMIT-ID>~1, mark it edit, remove the secret, git add ., git commit --amend, then git rebase --continue and push. An agent can’t drive an interactive editor reliably, so for an unattended run a non-interactive rewrite is the practical equivalent. This one drops the file from the two affected commits:

git filter-branch -f --index-filter 'git rm -q --cached --ignore-unmatch .env' HEAD~2..HEAD
git log --oneline
git log -p -S"not-a-real-key" --oneline | wc -l
c19c9f5 docs
394e87c add config
9f3f6ee app
0

The search now finds nothing. Note that every rewritten commit gets a new hash, and the key itself must be treated as leaked and rotated regardless. Anything that has been in a commit, even locally, has been written to disk in places like the reflog, and a push attempt has sent it to GitHub’s scanner. Rotation costs a few minutes. Not rotating costs whatever the key can reach.

(git filter-branch carries its own warnings in the git docs and suits a small local range like this. On a branch other people have pulled, rewriting history needs a conversation first.)

Don’t let the agent bypass it

Push protection has a bypass. The command-line docs describe opening the URL as the same user who pushed, choosing a reason (“It’s used in tests”, “It’s a false positive” or “I’ll fix it later”), clicking “Allow me to push this secret” and retrying within three hours. If you can’t bypass, you can ask for privileges, and a reviewer approves or denies by email. Per the web UI docs, those requests expire after 7 days.

The reasons aren’t equivalent. According to the docs, “It’s used in tests” and “It’s a false positive” close the resulting alert without notifying anyone, while “I’ll fix it later” keeps it open and notifies the commit author and repository administrators. An agent that picks a reason to get unstuck is making a security decision by picking from a short menu of excuses, and the quietest options are the ones that tell nobody.

The browser flow is human-shaped, but it isn’t the only door. The secret scanning REST API has a separate Create a push protection bypass endpoint. If the token an agent holds can call it, the block is a speed bump, not a control. Whether that’s true depends on your token scopes, so check them rather than assume.

Here’s a CLAUDE.md or AGENTS.md excerpt worth adding. It’s a recommendation, and a written rule is guidance the agent can ignore, so back it with token scopes.

If a push is rejected for a detected secret:
1. Stop. Do not retry the push.
2. List the commits and files named in the remote output.
3. Do not bypass push protection or request a bypass.
4. Report which key type was detected and tell the user it needs rotating.

Step 1 matters most. An agent that retries sees the same rejection, tries a variation, and eventually reaches for the amend that doesn’t work or a bypass that shouldn’t be used.

What this doesn’t cover

Push protection catches a supported pattern at the server. It doesn’t catch a key in a format GitHub doesn’t know, a key pasted into a prompt, or one committed to a private repository without secret scanning on. It also doesn’t stop the secret being written to disk in the first place. A prompt-side hook, like the one in this earlier Playbook , keeps secrets from leaving your terminal, and push protection is the second net behind it, not a replacement.

Try this week

Make a throwaway repository, commit a file containing a deliberately fake string in the style of a supported token, and push it to a private test repo with secret scanning and push protection switched on. Read the remote: output the way your agent would, then try the amend route and see whether the push clears. Ten minutes shows you exactly which instruction your agent files are missing.