
Plugin4Shell Lets Repo Owners Swap Pinned Plugin Code
Plugin4Shell Lets Repo Owners Swap Pinned Plugin Code
There is a safe-sounding practice in the AI coding-agent world: pin every plugin you install to the exact commit you reviewed, via its 40-character SHA. Review once, pin forever, never think about it again. Research published this week by AIR Security — by Or Nevo, Dor Granat and Niv Hoffman — shows that the pin was always an act of trust, not a lock. They call the flaw Plugin4Shell, and it lets the owner of a plugin's repository serve completely different code under that reviewed SHA across the four biggest coding agents: Claude Code, OpenAI Codex, GitHub Copilot and Gemini CLI.
The underlying mechanic is a git quirk older than any of these agents. And once you see it, it is impossible to unsee.
Verification theater: the checkout that never checks
Agents install marketplace plugins by cloning the repository and checking out the pinned commit:
git clone <plugin repo> ./
git checkout aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
The marketplace has decided that aaa…aaa is the reviewed, vetted version. The agent clones, the agent checks out — but as README verification, that is all. Nothing after the checkout confirms that the working tree actually sits on aaa…aaa.
Git offers the attacker a way to make that checkout land somewhere else while the pin still looks honoured. When a name is both a valid ref and an object id, git prefers the ref — and it only prints a refname is ambiguous warning. So if the repository owner creates a branch whose name is exactly the 40-hex pinned SHA, makes it the repository's default branch, and points it at malicious code, the plain git clone brings it down as a local branch, and git checkout <pinned-sha> resolves to the branch. The attacker's code is now in the working tree, and the agent reports a perfectly successful install at the pinned commit.
I reproduced this end-to-end in a scratch repo. Git's own git check-ref-format accepts a 40-hex branch name, and the checkout behavior is real:
$ git branch <pinned-sha> # attacker names a branch like the pin
$ git branch -f <pinned-sha> <malicious> # ...and points it at other code
$ git checkout <pinned-sha> # victim/agent "checks out" the pin
warning: refname '<pinned-sha>' is ambiguous.
$ git rev-parse HEAD # d645fe8... — the MALICIOUS commit
The pin names one commit; the working tree runs another. The warning is written to stderr and ignored by tooling, which is exactly why nobody noticed for years.
Two ways to get owned, both zero-click
AIR says the attacker does not even need to control the marketplace — only the repository behind a plugin people already trust. Two paths:
- Publish a benign plugin, then rug-pull it. Contribute something genuinely useful, let it pass review and get pinned, let it spread, then flip the pinned commit. AIR has shown this exact "trusted → viral → malicious" chain before with skills.
- Hijack a legitimate author's repository. Take over the repo a trusted plugin points at (their prior SkillJacking and RepoJacking work took control of 925 already-used skills), then force the malicious version onto every agent that has it installed.
What turns install-time doubt into a genuine zero-click is background auto-update, which is the default in Claude Code and Codex: when the marketplace bumps the pinned SHA for a legitimate update, the same flawed git checkout re-runs silently. The attacker's bbb…bbb branch is already the default, so the swap installs itself — no prompt, no click, nothing on screen. The victim's only precondition is having the (previously benign) plugin installed.
Once that plugin runs, it has the same access as the developer: local files, saved credentials, the systems you can log into. This is the AI agent threat model in the worst possible place — a tool you invited into your terminal.
Where it works — and the Gemini variant
The branch-name trick does not work on every host. GitHub rejects branch and tag names composed purely of a 40-hex SHA, per GitHub's own documentation, so a plugin installed from a GitHub repository is safe from this particular variant. The trick works on hosts that allow such names — Bitbucket and self-hosted git servers, both of which the agents officially support as marketplace backends. The Hacker News confirmed that every plugin in Anthropic's community catalog and the default Claude Code and Copilot catalogs points to a GitHub repository, which noticeably narrows the branch-trick exposure for those two agents.
Gemini CLI is a different story. Its installer runs a three-step dance:
git clone --depth 1 <plugin repo> ./
git fetch origin 41d0bc0a4aeb2fbf797dacea39e876d98c95024b
git checkout FETCH_HEAD
git checkout FETCH_HEAD does not have to read the fetched commit: if the repository's default branch is literally named FETCH_HEAD, the checkout resolves to that branch instead. GitHub's SHA-shaped-name rule does not clearly block the name FETCH_HEAD, so AIR says it is not established that installing a Gemini plugin from GitHub avoids the flaw. Gemini CLI is also the agent that will not be fixed.
Patch status: two fixed, one missing, one retired
The bug is resolved inside the agent, on the user's machine — no marketplace can patch it for everyone. Where things stand, per AIR and The Hacker News:
| Agent | Status | Action |
|---|---|---|
| Claude Code | Patched (2.1.179, Anthropic confirmed 2026-06-17) | Update and stay current |
| OpenAI Codex | Patched (0.146.0, verified 2026-08-12) | Update and stay current |
| GitHub Copilot | No fix shipped | No patch available |
| Gemini CLI | Deprecated, will not be fixed | Migrate to Antigravity |
AIR says it built working proof-of-concept exploits against all four agents in May 2026, disclosed to all four vendors in June 2026, and confirmed Anthropic's fix on 2026-06-17 and Google's "no fix" decision on 2026-08-04. OpenAI's own public fix confirms the mechanism: git "can interpret a requested commit SHA as a branch name", which can make a plugin source "materialize a different commit than the one it pinned".
As of The Hacker News's September 18 reporting, no CVE had been assigned, no vendor security advisory had been published, and there was no sign the flaw had been exploited in a real attack. Google stopped serving the consumer Gemini CLI in June and has been pointing users at Antigravity, which AIR says the attack cannot reach — but enterprise Code Assist builds continue to receive updates, and whether a Plugin4Shell fix is among them is not clear.
Also unclear: whether updating an affected agent sweeps out a plugin that was already swapped, or only stops future swaps. Assume only the latter until told otherwise.
The fix is one assertion
AIR's suggested closing line for both variants is a single shell check after checkout:
test "$(git rev-parse HEAD)" = "<pinned-sha>" || abort
Note it compares the resolved HEAD, not the ref that was requested — that distinction is exactly the seam the Gemini FETCH_HEAD variant slips through.
What this means beyond agents
Plugin4Shell joins a busy September for AI coding-assistant supply-chain incidents. The broader lesson is boring and worth repeating: pinning is not verification. Everywhere your pipeline does a pinned git checkout — agent plugins, build scripts, CI clones, dependency vendoring — add the verify-after-checkout line and fail loudly if the resolved HEAD does not match. Git's "ambiguous refname" default is a footgun with a viral name now, but it was always there; anything that git checkouts a hash without checking the result has been running on trust, not on a lock.
Sources
- AIR Security: Plugin4Shell — Zero Click RCE Vulnerability found in top 4 most popular coding agents
- The Hacker News: Plugin4Shell Lets Repository Owners Swap Pinned Plugin Code Across Four AI Coding Agents
- OpenAI: codex: Verify Git plugin SHA checkouts (fix PR)
- GitHub Docs: Dealing with special characters in branch and tag names
- Anthropic: claude-code v2.1.179 release
- OpenAI: codex rust-v0.146.0 release
// author
Editor & Operator
Mohd Firdaus Abu Bakar (MrDodgerX) is an enterprise geospatial systems engineer in Malaysia and the editor behind hejes.my. He runs the briefing pipeline, curates and edits every draft, verifies each factual claim against primary sources, and presses the publish button — nothing ships without his review. He writes about AI, security and Malaysia's tech scene from a builder's perspective. Professional profile and code: the links below.
// sources //
fact-check verified · 21 SEPT 2026 · hejes-content-fact-check
- Plugin4Shell — Zero Click RCE Vulnerability found in top 4 most popular coding agentsAIR Security · accessed 21 SEPT 2026
- Plugin4Shell Lets Repository Owners Swap Pinned Plugin Code Across Four AI Coding AgentsThe Hacker News · accessed 21 SEPT 2026
- codex: Verify Git plugin SHA checkouts (fix PR)OpenAI · accessed 21 SEPT 2026
- Dealing with special characters in branch and tag namesGitHub Docs · accessed 21 SEPT 2026
- claude-code v2.1.179 releaseAnthropic · accessed 21 SEPT 2026
- codex rust-v0.146.0 releaseOpenAI · accessed 21 SEPT 2026
related //

ARTEX AI: The Pentest Tool That Hit Korean Banks
An open-source Chinese AI pentest tool was used to breach at least seven South Korean banks. The attacker's own Claude Code logs gave them away.

15,465 MCP Servers and Almost No Governance
OX Security mapped 15,465 public MCP servers: 15.6% hosted outside the US, 2.3% of domains already dead. The protocol is fine. The trust isn't.

Placeholder domains now serve malware. Audit your docs.
third-party.com, the docs placeholder everyone copied, now serves ClickFix; 13 lookalike domains carry scams. Static checks miss all of it — audit your docs.
// join the feed
one fresh insight per week. no spam, ever.
weekly digest. unsubscribe anytime.