Skip to content
AI security11 min read

Plugin4Shell: What We Found When We Checked Our Own Stack

Plugin4Shell: how a branch named like a commit hash bypasses SHA pinning in AI coding agents
Plugin4Shell: how a branch named like a commit hash bypasses SHA pinning in AI coding agents

We run Claude Code and Codex across most of our client engagements at this point. So when a vulnerability with the name Plugin4Shell started showing up in our security feeds last week, we didn't just read the writeups. We pulled up our own environments and checked.

What we found is a good reminder that "pinned" is a word that promises more than most tools actually deliver on.

What Happened

Coding agents install plugins from marketplaces. To keep that safe, the marketplace locks a plugin to one specific, reviewed commit, identified by its SHA, a 40 character hash. The idea is simple: once a plugin passes review, it can't quietly change underneath you.

On September 17, security researchers at AIR disclosed that four of the biggest AI coding agents on the market, Claude Code, OpenAI's Codex, GitHub Copilot, and Gemini CLI, all shared the same gap. They check out the pinned commit. They never verify they actually landed on it. Zero clicks required. No approval prompt. If you had a plugin installed from a marketplace you trusted, reviewed and pinned exactly the way the security model intends, you were exposed anyway.

We wanted to know where that left us before we took anyone else's word for it.

Why This Matters

This isn't a niche tool problem anymore. According to the JetBrains 2026 Developer Ecosystem Survey of over 24,500 developers, 85% of developers now regularly use AI tools for coding. Gartner puts the enterprise AI coding agent market at roughly $9.8 to $11 billion annualized as of April 2026. And the plugin ecosystem underneath these agents runs on the Model Context Protocol, which has crossed 9,400 public servers, the connective tissue that lets agents reach tools, data, and each other (treat that server count as directional, it's an aggregator estimate, not an independent census).

There's one more number worth sitting with. Anthropic's own data shows average coding agent session length grew from 4 minutes in Q1 2025 to 23 minutes by Q1 2026. Agents are running longer, doing more unattended work, with less of a human in the loop watching each step. That's exactly the condition Plugin4Shell needs. The exploit runs through background auto-updates. Nobody has to click anything for the swap to happen.

Put those together and here's what you actually get: a lot of engineering teams running tools that update themselves quietly, with nobody watching. Built on a trust mechanism that four separate companies happened to get wrong the exact same way.

How It Works

Here's the part most coverage glosses over, and it's worth being precise about because the mechanism is genuinely simple once you see it.

Git resolves references, meaning branch and tag names, before it resolves raw commit objects. Normally that doesn't matter, because branch names don't look anything like 40-character hex strings. But nothing stops a repository owner from naming a branch with exactly the same 40 characters as a commit hash.

So the attack looks like this. A plugin gets reviewed. The marketplace pins it to commit a1b2c3... (a real 40-hex-character SHA). Later, the attacker, who controls that plugin's repository, creates a branch literally named a1b2c3..., matching the pinned hash, and sets it as the default branch. When the agent runs its install or update step, it does something close to this:

git clone https://github.com/example/plugin-repo.git
git checkout a1b2c3d4e5f6...

Because Git sees a reference matching that name before it looks for a raw commit object, git checkout resolves to the malicious branch instead of the real pinned commit. The agent's config still shows the same hash string. The UI still reports the plugin as pinned and verified. The code that actually runs is whatever the attacker put on that branch.

Three of the four affected agents, Claude Code, Codex, and Copilot, follow that same two-step pattern: clone, then checkout, with nothing in between confirming the working tree actually matches the pinned hash. That missing confirmation step is the entire vulnerability.

Diagram

One detail that matters for anyone deciding how worried to be: GitHub blocks branch or tag names shaped like commit hashes on its own platform. So a plugin hosted directly on GitHub isn't exposed to this specific trick. AIR's position is that marketplaces built on other hosts, Bitbucket among them, remain vulnerable, and that GitHub Copilot supports those external marketplace sources, which is part of why Copilot's exposure is still open.

Timeline and Patch Status

DateEvent
May 2026AIR develops working proof-of-concept exploits against all four agents
June 2026Disclosed privately to Anthropic, OpenAI, Microsoft, and Google
Sept 17, 2026Public disclosure
Sept 18, 2026Security press coverage begins

Patch status as of the week this post is published:

AgentStatusNotes
Claude CodePatchedVersion 2.1.179 and later
CodexPatchedVersion 0.146.0 and later
GitHub CopilotNo client-side fixGitHub says a platform-level mitigation exists, the branch-naming block, but AIR disputes that it covers the full exposure, since Copilot also supports external marketplaces GitHub's block doesn't reach
Gemini CLIWill not be patchedGoogle is deprecating the product and pointing users to Antigravity CLI instead

No CVE has been assigned as of this writing, and there's no confirmed real-world exploitation. This is a disclosed vulnerability, not a known breach. Two of the four fixes shipped fast. The other two aren't coming, for very different reasons.

What We Checked

We treated this the same way we'd treat it for a client engagement: don't trust the changelog, go check the actual running version.

The check itself is simple enough that any team can run it in a few minutes:

# Check installed Claude Code version
claude --version
 
# Check installed Codex version
codex --version
 
# Compare against the fixed versions
# Claude Code: 2.1.179 or later
# Codex: 0.146.0 or later

For teams managing this across more than a handful of machines, that check is worth scripting rather than running by hand:

#!/bin/bash
# plugin4shell-check.sh
# Compares installed agent versions against the known-fixed versions
 
CLAUDE_FIXED="2.1.179"
CODEX_FIXED="0.146.0"
 
claude_version=$(claude --version 2>/dev/null)
codex_version=$(codex --version 2>/dev/null)
 
echo "Claude Code: $claude_version (fixed version is $CLAUDE_FIXED or later)"
echo "Codex: $codex_version (fixed version is $CODEX_FIXED or later)"
echo ""
echo "GitHub Copilot: no client fix available, check plugin marketplace sources"
echo "Gemini CLI: no fix planned, migration to Antigravity CLI recommended"

Placeholder section, replace before publishing. Here's the shape the results took once we ran the script across our environments. Most of the Claude Code installs were already sitting on a fixed version by the time we checked, auto-update had done most of the work quietly in the background. Codex told a similar story. The exceptions were a handful of machines where auto-update had been turned off for unrelated reasons, which is its own small lesson about what else gets missed when you disable something for one purpose and forget it affects everything else.

GitHub Copilot and Gemini CLI were a different exercise entirely, since neither has a real fix to check a version number against yet. What we tracked there wasn't patch status, it was exposure: how many environments still pulled plugins from marketplaces outside GitHub, and how many still had Gemini CLI installed with plugins actively in use at all.

Fill in your own numbers here before this goes live. [X] of [Y] Claude Code installs on a fixed version. [X] of [Y] Codex installs on a fixed version. [X] environments exposed through non-GitHub Copilot marketplaces. [X] environments still running Gemini CLI with active plugin usage.

That's the real audit format. The point of running the script yourself isn't to confirm what this post already claims, it's to find out something you didn't already know.

Automating the Check

Running the check by hand works fine for a one-time pass. If you want this caught the moment a new disclosure like this drops, the shape of an automated pipeline looks something like this. This is a reference architecture, not a claim about our exact internal setup, swap in whatever ticketing, inventory, and alerting tools you actually run.

Diagram

The parts worth building even if the rest stays manual: an inventory that knows every machine running a coding agent, and a comparison step against known-fixed versions that runs on a schedule, not just when a new headline reminds you to check.

Diagram

What's Still Not Fixed

Being honest about the gaps matters more here than usual, because half the affected vendors don't have a real fix.

GitHub Copilot's situation is the messiest. GitHub's stated mitigation only covers repositories hosted on GitHub itself. If your organization uses Copilot with plugins pulled from Bitbucket or any other host, that mitigation does nothing for you, and there's no committed timeline for a client-side fix.

Gemini CLI isn't getting patched at all. Google's answer is migration, not remediation, which is a reasonable business decision but leaves anyone who hasn't migrated yet sitting on an unpatched, zero-click RCE with no forthcoming update.

There's also a structural point worth naming plainly: the fix has to live in the agent itself. A marketplace can't protect its users on its own, no matter how carefully it reviews plugins, because the vulnerability is in how the agent verifies what it checked out, not in what the marketplace approved. That means trusting a well-run marketplace isn't enough. You have to trust the client too, and right now two of the four major clients don't earn that trust yet.

And because no CVE has been issued, this won't show up automatically in most vulnerability scanners or dependency audits. If your security tooling relies on CVE feeds to flag exposure, it's blind to this one for now.

The Bigger Picture

What stuck with us isn't really the git mechanics, clever as they are. It's that four separate engineering teams, at four separate companies, built the same trust assumption into their auto-update pipeline and none of them caught it internally. An outside lab did.

That's not really a story about one bad line of code. It's a story about what happens when "pinned" becomes a word teams trust without a team regularly checking whether the tooling backs it up. SHA pinning was the industry's answer to exactly this kind of risk, and it turned out the answer was only as strong as the one verification step nobody added.

If there's a takeaway beyond patching your own agents, it's this: any system in your stack that claims to lock something down by a hash or a version string deserves the same question asked of it. Does it actually verify the thing it checked out, or does it just check the label and trust the rest.

References

  1. AIR Security — Plugin4Shell disclosure
  2. The Hacker News — Plugin4Shell lets repository owners bypass SHA pinning
  3. Help Net Security — Plugin4Shell: vendor patch status
  4. The Register — AI coding agents hit by zero-click plugin flaw
  5. OpenAI — Codex patch pull request #34644
  6. JetBrains — 2026 Developer Ecosystem Survey, AI tool adoption data
  7. Gartner — Enterprise AI coding agent market sizing

Related.