← Back to BlogSecurity

Plugin4Shell: The Zero-Click Bug in Four AI Coding Agents

A git ref-resolution bug let attackers swap SHA-pinned plugins in Claude Code, Codex, Copilot, and Gemini CLI with zero user action.

Air Security disclosed a vulnerability called Plugin4Shell on September 17, 2026: a zero-click remote code execution flaw in the plugin systems of four major AI coding agents, Claude Code, OpenAI Codex, GitHub Copilot, and Gemini CLI. The bug matters because it defeats the one control teams already trust to stop exactly this kind of attack, pinning a plugin dependency to an exact git commit hash, and it defeats that control silently, with the agent still reporting the hash it was told to check out.

Sources: Air Security, Help Net Security

Researchers Or Nevo, Dor Granat, and Niv Hoffman built working exploits against all four agents in May 2026, reported the flaw to each vendor in June, and published their findings publicly in September once the disclosure window closed. Coverage from The Hacker News, Help Net Security, and The Register confirmed the same core mechanism and the same four affected agents within a day of the original post. As of publication, no CVE identifier had been assigned to Plugin4Shell, and no vendor had issued a formal security advisory alongside its patch.

Sources: The Hacker News, The Register

SHA pinning was supposed to make this impossible

Pinning a dependency to a commit hash instead of a branch name is standard supply-chain advice, repeated in nearly every checklist written for teams that pull third-party code automatically. A branch can move. Someone can force-push over it, merge something unreviewed into it, or quietly repoint it at different code entirely. A commit hash is supposed to be immutable: it identifies one specific tree of files, computed from the content itself, so changing even one byte produces a different hash. Pin to the hash, the advice goes, and nothing upstream can change what lands in your codebase without you noticing, because the hash you asked for and the code you got will always match.

How a matching branch name beats a commit hash

Plugin4Shell shows that promise depends on an assumption none of the four agents actually checked: that resolving a 40-character SHA always returns the commit object with that hash, and nothing else. Git does not guarantee that. A repository can contain a branch, or any other ref, whose name happens to be the same 40-character string as a commit hash. When that happens and a command asks git to resolve that string, git does not automatically prefer the raw object. Air Security's own writeup of the bug puts it plainly: "when a name is both a valid ref and an object id, git prefers the ref and only prints a refname is ambiguous warning." An attacker who controls the plugin's repository can create a branch named exactly after the commit hash the marketplace has pinned, point that branch at whatever code they want, and every subsequent checkout of that "pinned" hash resolves to the branch instead of the original commit object, silently.

Sources: Air Security

The specific command each agent runs decides exactly how the swap lands. Claude Code, Codex, and GitHub Copilot clone the plugin repository and then run a plain git checkout against the pinned SHA, so a same-named branch on the default remote is enough. Gemini CLI takes a different path, fetching the pinned commit directly with git fetch origin [SHA] and then checking out FETCH_HEAD, which sidesteps the branch-name collision on the SHA itself but opens the identical failure if a repository's default branch happens to be named FETCH_HEAD, since that checkout resolves to the branch rather than the commit git just fetched. Different command, same root cause: a checkout that trusts whatever ref git resolves the name to, without ever comparing the resulting HEAD back against the SHA the marketplace told it to install.

Sources: Air Security

  • Publish a legitimate, useful plugin to a marketplace or catalog and let it accumulate real installs over weeks or months.
  • Once the marketplace has pinned a specific commit hash for that plugin, create a branch in the plugin repository named after that exact 40-character hash.
  • Point that branch's tree at malicious code instead of the reviewed version the marketplace actually approved.
  • Wait for each installed agent to run its next scheduled plugin update, the same background process that normally keeps plugins current.
  • The agent checks out what it believes is the pinned commit, lands on the attacker's branch instead, and still reports the original SHA as installed.

The marketplace itself never does anything wrong in this sequence. It records the correct, legitimate commit hash the first time the plugin is reviewed, and that record does not change. The failure happens entirely on the client side, inside the agent, at the moment it turns that recorded hash into an actual checkout. A marketplace-side integrity check, a signature on the catalog entry, a review of the code at submission time, none of that helps if the step that finally pulls the code onto a developer's machine can be redirected without anyone re-verifying what came back. That is what makes Plugin4Shell a client-verification bug rather than a marketplace-trust bug, and it is why patching it required a code change inside each agent rather than a policy change at the catalog layer.

A long exposure window, no confirmed breach yet

Air Security says it built working proof-of-concept exploits against all four agents in May 2026 and reported the flaw to each vendor the following month, under a standard responsible-disclosure timeline. Neither The Hacker News nor Help Net Security reported any confirmed case of the technique being used against a real developer before the public disclosure in September. That is a meaningfully different situation from a vulnerability already caught in active use. It does not make the exposure window short: Anthropic shipped Claude Code 2.1.179 about a month after the May proof-of-concept, and OpenAI took roughly three months to ship Codex 0.146.0. GitHub Copilot and Gemini CLI users are still inside that window, with no patch date in sight for either agent.

Sources: The Hacker News, Air Security

Zero clicks: the swap rides the auto-updater

None of this requires a developer to install a new plugin, approve an update, or click through a prompt. Claude Code and Codex both update installed plugins in the background by default, so the same checkout logic that installed the plugin the first time re-runs on its own schedule and re-resolves the pinned hash every time. That is what makes it zero-click: the vulnerable step fires automatically, on a machine the developer already trusted weeks earlier, with no new plugin to approve and no prompt to click through. Cybersecurity News described the same mechanism, tying the flaw's zero-click nature directly to that default background auto-update behavior in both agents.

Sources: The Hacker News, Cybersecurity News

Four vendors, four different responses

With the same underlying git behavior implicated across four unrelated codebases, the four vendors did not converge on the same fix. Two patched, on very different timelines; one has shipped no fix at all; and one retired the product rather than fix it.

AgentVendorResponsePatched Version
Claude CodeAnthropicPatched2.1.179
CodexOpenAIPatched0.146.0
GitHub CopilotMicrosoftNo fix shipped as of disclosureN/A
Gemini CLIGoogleDeprecated rather than patchedN/A

The exposure is not identical across the four. Air Security's own report notes that the default and community plugin catalogs for Claude Code and Copilot point to repositories hosted on GitHub, and GitHub's own hosting blocks this specific ref-collision attack, which narrows the practical risk to plugins pulled from self-hosted git servers, Bitbucket, or any source outside the vetted default catalog. That narrower exposure does not make GitHub Copilot's lack of a patch a minor detail. Microsoft says roughly 90 percent of Fortune 100 companies use GitHub Copilot, which means an unpatched checkout path sits underneath a large share of enterprise development regardless of which specific plugin sources those enterprises use. Google's response was the most drastic: rather than patch Gemini CLI, the company deprecated it, leaving existing installations vulnerable indefinitely and pushing the fix onto whatever migration path users choose next.

Sources: The Hacker News, Help Net Security

Why this breaks the SHA-pinning model specifically

The uncomfortable part of Plugin4Shell is not that a clever attacker found a git edge case. It is that the failure sits exactly where a supply-chain control is supposed to be strongest: the verification step, not the install step. Every one of the four agents did the thing SHA pinning asks for, asking git to resolve a specific commit hash before installing anything. What none of them did was confirm, after the checkout finished, that the working tree it produced actually matched the commit object that hash names. They trusted that asking for the right label would produce the right artifact, and treated the label itself as proof. The same failure mode shows up anywhere a system checks a label instead of the object the label describes: a checksum recorded once but never recomputed against the bytes actually downloaded, a container image referenced by a tag a registry lets move, a signature verified against a key file rather than against the artifact the key was supposed to sign. A SHA pin, a checksum, and a digital signature are all just claims about an artifact. None of them protect anything unless something independently checks the claim against the artifact itself, every time, not only once at write time.

Insight

SHA pinning is not a broken idea. It is an unenforced one, four times over, in exactly the systems built to make third-party code trustworthy by default.

Plugin4Shell is not the first 2026 disclosure to target the distribution layer underneath AI coding agents rather than the agents' own reasoning. In May 2026, researchers separately found agents linked to OpenAI's own testing systems flooding the RubyGems registry with more than 2,000 malicious packages, exploiting gaps in account verification and a documentation-build process rather than a broken pin. The mechanisms do not overlap. What both cases share is where the failure lived: underneath the agent, in the plumbing that decides what code actually reaches an installed system, a layer most teams assume is solid because nobody told them to check it.

Pro Tip

Until GitHub Copilot ships a fix and Gemini CLI users complete a migration, treat both as permanently unpatched. Restrict plugin installs to vetted default marketplace catalogs rather than self-hosted or third-party git sources, and where the agent allows it, turn off background plugin auto-update and update on a schedule a person actually reviews. For any plugin already pinned to a SHA, run git rev-parse HEAD after checkout and compare it against the pinned hash directly, rather than trusting the checkout command's exit code.

Where TLM Forge fits, and where it does not

TLM Forge does not operate at the plugin-installation layer Plugin4Shell exploits, so it would not have caught a git checkout silently landing on a different commit than the one an agent was told to pin. What it is built around is the pattern underneath that failure: a check that reports success without anything independent confirming what it actually verified. Before an agent writes code, TLM Forge requires a spec and goal contract signed off in advance. After the agent finishes, an independent reviewer reads the resulting diff in fresh context rather than taking the agent's own summary of its changes on faith, a red-team pass specifically hunts for the class of issue that looks handled and is not, and the convergence gate blocks the merge until every finding clears, not until a self-report says the work is done. Plugin4Shell is a reminder of why that distinction has to hold outside the merge gate too. A pinned SHA is a claim about an artifact. Something has to check the claim against the artifact itself, or the claim is just a label nobody is reading.

Frequently asked questions

01What is Plugin4Shell?

A zero-click remote code execution vulnerability, disclosed by Air Security on September 17, 2026, affecting the plugin auto-update mechanisms of Claude Code, OpenAI Codex, GitHub Copilot, and Gemini CLI. It lets an attacker who controls a plugin repository swap in malicious code while the agent still reports the correct pinned commit hash.

02Is Claude Code still vulnerable to Plugin4Shell?

No. Anthropic patched the flaw in Claude Code version 2.1.179. Teams running an older version, or with automatic plugin updates disabled since before that release, should update and confirm the installed version number directly.

03Does Plugin4Shell have a CVE number?

No. As of its September 2026 disclosure, no CVE identifier had been assigned to Plugin4Shell, and none of the four affected vendors had published a separate formal security advisory alongside their fix or response.

04Why is Plugin4Shell called a zero-click vulnerability?

Because the malicious swap happens through the agent's default background plugin auto-updater. Once an attacker changes what a pinned commit hash resolves to, every agent with that plugin installed pulls the malicious version on its next automatic update, with no click from the user.

05How does SHA pinning normally protect against malicious plugin updates?

It locks a dependency to one specific, immutable commit object instead of a moving branch, so an upstream change cannot silently affect installed code. Plugin4Shell defeated it because the agents checked out the pinned SHA but never verified the resulting tree actually matched that commit object.

Ship AI-written code you can trust

TLM Forge is the missing process layer for Claude Code: a spec audit, independent multi-agent review, enforced TDD, and an adversarial red-team gate.

Get TLM Forge