Your coding agent's plugins are pinned to 40-character commit SHAs, and those pins have been holding for a reason you did not choose. They held because GitHub refuses to create a branch whose name looks like a commit hash — not because your agent ever checked that the code it ran matched the commit you reviewed.
That is the practical shape of Plugin4Shell, the plugin supply-chain bug Air Security disclosed on 17 September. It affects Claude Code, OpenAI Codex, GitHub Copilot CLI and the Gemini CLI. Two of the four have shipped client fixes. The mitigation that actually covers everyone else is server-side, sits on a Git host, and applies only if your plugin marketplace repositories live on the right one. So the remediation question for a platform lead this week is not "which agent version are we on." It is a two-column inventory: agent version, and the Git host behind every marketplace your engineers have added.
The Pin Was Checked Out, Never Verified
The bug is a missing assertion, not a cryptographic break. All four agents resolve a pinned plugin by running a git checkout against the 40-hex SHA and then trusting whatever landed in the working tree. Air's write-up puts it plainly: the agent "check[s] out the pinned commit but never checks that it actually landed there."
That gap matters because Git is permissive about what a ref may be called: nothing in Git stops a repository from carrying a branch named a1b2c3…, which is precisely why both large hosts ban the shape. But the resolution rules do not cut the way the pin's users tend to assume. Asked for a full 40-character hash, Git returns the object and merely warns that a same-named ref "will be ignored when you just specify 40-hex" — the precedence runs toward the commit, not the branch. The ordering that gitrevisions does publish for a <refname> — $GIT_DIR/<refname>, then refs/<refname>, then refs/tags/, then refs/heads/, then refs/remotes/ — is about disambiguating refs from each other, not refs from commits. So this is not a pinned hash being shadowed at resolution time. It is staged one step earlier, on the branch a clone checks out, and it survives because nothing afterwards compares HEAD to the pin.
Air describes two variants. Against Claude Code, Codex and Copilot, an attacker who controls the plugin repository creates a branch whose name is the exact pinned SHA and makes it the default branch. Against the Gemini CLI, the repository's default branch is named FETCH_HEAD, so the agent's git checkout FETCH_HEAD resolves to the branch and the correctly-fetched commit is silently discarded.
The reason this earns the "zero-click" label is the update path, not the install path. As Air told Help Net Security, "This is not only an install-time bug... so when the marketplace bumps the pinned SHA, the swap reaches already-installed plugins with no user action." Anthropic's own documentation confirms the machinery: a background refresh checks the marketplace remote for new commits, and when it finds them, "Claude Code clones the marketplace again and swaps the new clone in." Nobody types anything. As of 18 September no CVE had been assigned and no in-the-wild exploitation had been reported.
GitHub Blocks It. So Does GitLab.
The host-side control that neutralises the branch-name variant exists on both of the two largest Git hosts, and most coverage has only credited one of them. GitHub's documentation states the restriction directly: no branch or tag names "which look like Git object IDs (40 characters containing only 0-9 and A-F), to prevent confusion with actual Git object IDs." GitHub told The Register that "To prevent abuse of SHAs, GitHub does not allow users to create branch or tag names that resemble commit SHAs," and that "this mitigation ensures the reported vulnerability cannot be exploited on GitHub."
At least one write-up then grouped GitLab with Bitbucket and self-hosted platforms as hosts with no such protection. That is wrong, and it changes the remediation list. GitLab's branch documentation carries the same rule in almost the same words: "Branch names with 40 hexadecimal characters are prohibited, because they are similar to Git commit hashes." GitLab shipped that validation as a push-side check — strictly enough that users have filed bugs about being unable to delete pre-existing 40-hex branches, because the rule fires on any push touching the ref.
So the host axis sorts into three buckets rather than two. GitHub and GitLab reject the malicious branch name outright. Bitbucket, per Air, permits it. Any self-hosted Git server — Bitbucket Data Center, Gitea, Forgejo, a bare repo behind SSH — permits it unless someone wrote a pre-receive hook that rejects 40-hex refs, and almost nobody has, because until this week there was no reason to.
Two things follow that are easy to get backwards. First, the host block is not a patch and does not fix the FETCH_HEAD variant, which does not need a hash-shaped branch name at all. Second, a host block is a control you do not own: if your marketplace moves, or a team stands up an internal mirror, your protection moves with it and nobody files a ticket.
Your Official Marketplace Is Fine. Your Internal One Is the Problem.
The exposure here is concentrated almost entirely in privately-hosted marketplaces, which is precisely the thing a large engineering organisation builds. The Hacker News checked the public catalogs and found that every plugin in Anthropic's community catalog and in the default catalogs for Claude Code and Copilot points at a GitHub repository — which means the host block covers them and practical exposure for anyone living on official sources is small.
That is the strongest version of the vendors' position, and it is basically right for individual developers. It is close to useless for an enterprise, because the whole point of an internal marketplace is that it is not on the public catalog. And both vendors explicitly support putting it somewhere else. Anthropic's marketplace documentation says "Any git hosting service works, such as GitLab, Bitbucket, and self-hosted servers," with the example /plugin marketplace add https://gitlab.com/company/plugins.git. GitHub's own Copilot CLI docs say marketplaces "can be located on GitHub.com, in any other online Git hosting service, or on your local or shared file system", and the worked example in the install guide is copilot plugin marketplace add https://gitlab.com/OWNER/REPO.git.
This is a supported, documented, recommended-for-privacy configuration. It is not an edge case somebody abused. If your security team approved coding-agent plugins on the argument that everything is pinned to a reviewed SHA — and that argument was made in a lot of review boards this year — the pin was doing less work than the slide claimed, and the compensating control turned out to be a property of whichever host the repo happened to sit on.
The same inversion applies to the threat model. Plugin4Shell requires the plugin repository owner, or someone who has taken over that repository, to stage the branch. For a public marketplace that is a hostile third party. For an internal marketplace it is anyone with push access to a repo that probably has looser branch protection than your production services, because it "only holds prompts and scripts." We made that argument about plugin commit contents three weeks ago; this is the same repository, failing a different way.
Google Will Keep Supporting Gemini CLI. It Will Not Fix This.
The sharpest enterprise detail in this disclosure is that Google's refusal to patch and Google's promise of continued support are both true at the same time, and they apply to overlapping populations. Air reports that Google confirmed on 4 August it would ship no fix, because the Gemini CLI is deprecated and users should migrate to Antigravity.
But read Google's own transition notice. The 18 June 2026 shutoff applies to Google AI Pro and Ultra subscribers and free Gemini Code Assist for individuals. For organisations, it says something else entirely: "If your organization uses Gemini CLI or our IDE extensions via a Gemini Code Assist Standard or Enterprise license... your access remains unchanged. We'll continue to support Gemini CLI and Gemini Code Assist with access to the latest Gemini models and other updates."
So the individual hobbyist was cut off in June. The enterprise with a Code Assist Standard or Enterprise licence is still running a product Google describes as supported, receiving model updates, carrying a zero-click RCE its vendor has said it will not fix. That is not a contradiction Google has acknowledged anywhere, and the notice does not address security maintenance either way. If you hold that licence, treat "supported" as a billing statement, not a patching commitment, and ask your account team in writing whether Plugin4Shell specifically will be fixed for licensed customers.
Migration is the obvious answer and it is not free. Antigravity's admin URL allowlist was not honoured by its browser subagent as recently as last month, so moving off an unpatched CLI onto a replacement whose own controls have slipped is a trade, not an upgrade. Make it deliberately.
GitHub Copilot sits in a different but not better position: no client fix as of disclosure, and a documented ability to add marketplaces from hosts where the branch trick works.
The Inventory Has Two Columns
Agent version alone does not tell you whether you are exposed; you need the cross-product of agent version and marketplace host. Build the table before you build the remediation plan, because the answer differs per row.
Patched clients, per Air's disclosure timeline: Claude Code 2.1.179, which Anthropic confirmed on 17 June; and OpenAI Codex 0.146.0, released 29 July with the release-note line "Verify Git plugin SHA checkouts" and verified fixed by Air on 12 August. Unpatched: Copilot CLI and Gemini CLI.
On the host column, the controls are better than most teams realise and nobody has turned them on, because until this week they looked like inventory hygiene rather than a security boundary.
Claude Code ships a managed-settings allowlist, strictKnownMarketplaces, and its enforcement point is exactly right for this bug: restrictions "are checked before any network or filesystem operation," and "the check runs on marketplace add and on plugin install, update, refresh, and auto-update." An empty array blocks everything including Anthropic's official marketplace. A hostPattern entry constrains marketplaces to a host you name by regex. That is the lever: allowlist the hosts that reject SHA-like refs, and GitHub's server-side block becomes a control you are deliberately relying on instead of one you are accidentally benefiting from. blockedMarketplaces handles the inverse case, and supports the owner/* wildcard from v2.1.223.
Copilot CLI's sha field is documented as "must be a full 40-character commit SHA" and is recommended to "pin to a sha for reproducible installs that are immune to force-pushes or tag/branch moves" — the guarantee Plugin4Shell voids. Since there is no client fix, the available control is the update path: autoUpdate in settings, or COPILOT_AUTO_UPDATE=false, which turns a silent swap into a command someone has to run — though GitHub documents that auto-update as covering first-party plugins from the built-in marketplaces.
Three Checks Before Your Next Agent Session
This Week:
- Enumerate marketplaces, not agents. Collect
extraKnownMarketplacesfrom every managed, project and user settings file, plus the output ofcopilot plugin marketplace list. Sort the results by Git host. Anything not on github.com or gitlab.com is your exposed set, and it will be shorter than you fear and more interesting than you expect. - Verify one pin by hand. In an installed plugin's checkout, run
git rev-parse HEADand compare it to the SHA declared in the marketplace manifest. This is the assertion the agents skipped; doing it once tells you whether the pin was ever real in your environment. - Upgrade the two that have fixes. Claude Code to 2.1.179 or later, Codex to 0.146.0 or later. Do this even if every marketplace you own is on GitHub — the host block is not yours to keep.
This Month:
- Set
strictKnownMarketplacesin managed settings, scoped to the hosts and repositories you have actually reviewed. Then re-run check 1 and confirm a non-allowlisted marketplace is refused on update, not just on add. - Turn off unattended plugin updates where no client fix exists.
COPILOT_AUTO_UPDATE=falsefor Copilot CLI. For the Gemini CLI, plan the Antigravity migration with its own control gaps written into the plan, not discovered after. - Apply branch protection to plugin repositories at the tier you use for production code. A pre-receive hook rejecting 40-hex ref names is four lines and is the only thing standing between a self-hosted marketplace and this attack.
Before Renewal:
- Get the Gemini Code Assist answer in writing. If Google is billing you for a supported product, ask whether the support includes security fixes for disclosed RCEs, and put the response in the file you take to renewal.
The Bottom Line
Every supply-chain control the industry has adopted in the last decade — lockfiles, pinned digests, signed artifacts — works because something verifies the thing you got is the thing you named. Plugin4Shell is what happens when a system performs the ritual and skips the verification: the manifest said SHA, the UI said SHA, the checkout said SHA, and the bytes on disk were whatever the repository owner put on a branch. We have watched this failure mode arrive by hook, by docs build, by unpinned MCP server and by a hub that simply shut down. The agent layer keeps reinventing package management without reinventing its verification.
The uncomfortable part is not that four vendors made the same mistake. It is that the thing protecting most of us was a GitHub naming rule written to stop refs being confused with object IDs, long before anyone pinned an agent plugin to a SHA — and nobody in your review board knew they were depending on it.
A pin you did not verify is a preference.
Continue Reading
- Your Agent Allowlist Says Python. It Means Any Command.
- Only 16% of 'Docs' Commits Were Docs. Re-Tier Review.
- Antigravity's Allowlist Isn't Honored. Use Your Proxy.
- Guardrails' Hub Died Aug 25. Harvey Bought the Team.
- npm Pulled the Packages. Your Agent Config Reinfects You.
- OpenAI-Linked Agents Ran Code via a Docs Build. Sandbox Yours.
- Claude Code Stops Asking Aug 14. Prompts Aren't Policy.
- 84% Use AI Coding Tools. Every Sandbox Just Broke.
