Back to Blog

A Pinned SHA Is Not a Verified SHA: Lessons from Plugin4Shell

Plugin4Shell showed Claude Code, Codex, Copilot and Gemini CLI pinned plugins by commit but never checked what Git actually checked out. What to update, audit and fix.

Umut Korkmaz

Published: 4 min read

On September 17, security startup AIR published Plugin4Shell, a zero-click remote code execution flaw in how four popular coding agents install plugins: Claude Code, OpenAI Codex, GitHub Copilot and Gemini CLI. All four let a marketplace pin a plugin to a vetted commit. None of them checked that the commit they ended up running was the one they pinned. The lesson reaches well past AI plugins.

How the bypass works

Pinning by commit SHA is supposed to be the safe option. A marketplace vets a commit, writes its 40-character hash into the manifest, and every install checks out exactly that code. Claude Code's marketplace docs expose this as a sha field that takes precedence over ref.

According to AIR, Claude Code, Codex and Copilot ran git clone followed by git checkout <pinned-sha>. The catch: some Git hosts accept a branch whose name looks like a commit hash, and when a name matches both a ref and a commit, Git prefers the ref and only prints a refname is ambiguous warning. An attacker who controls the plugin repository creates a branch named after the pinned hash, points it at malicious code and makes it the default branch. The agent asks for the commit and gets the branch.

AIR's attack chain is patient: publish a benign plugin, get it pinned and installed, ship a benign update so the marketplace re-pins, then plant a branch named after the new pin. Background auto-update, which AIR says is the default in Claude Code and Codex, re-runs the checkout without anyone clicking anything. Gemini CLI had a variant: it fetched the SHA and then ran git checkout FETCH_HEAD, and a default branch named FETCH_HEAD won.

GitHub rejects 40-hex branch names outright, so the trick needs a host that allows them. AIR notes that Bitbucket and any self-hosted Git server do, and Claude Code's plugin docs accept Git URLs from both.

Where each vendor stands

AIR found the flaw in May and disclosed it to vendors in June. Per its timeline and InfoWorld's coverage:

  • Claude Code: fixed in 2.1.179, confirmed by Anthropic on June 17, 2026.
  • Codex: fixed in 0.146.0, verified on August 12, 2026.
  • GitHub Copilot: no fix shipped. GitHub told The Register it already restricts version or tag names that resemble commit SHAs, which it says blocks the attack on GitHub and its marketplace. AIR says that may not be enough, since marketplaces can live elsewhere, such as Bitbucket.
  • Gemini CLI: Google has deprecated it and confirmed on August 4 that no fix will ship. AIR advises moving to Antigravity.

Pinned is not verified

A pin is an instruction. Verification is reading back the result. AIR's recommended fix is one assertion after checkout that compares the resolved HEAD, not the requested ref, with the pin. Spelled out:

git checkout --quiet "$PINNED_SHA"
actual="$(git rev-parse HEAD)"
if [ "$actual" != "$PINNED_SHA" ]; then
  echo "pin mismatch: wanted $PINNED_SHA, got $actual" >&2
  exit 1
fi

Store the full 40-character SHA: rev-parse HEAD returns the full hash, so an abbreviated pin fails closed.

AIR stresses that this check has to live in the agent, because the pin is resolved on the client; a marketplace cannot close the gap alone. Every vetting process built on pinning inherits it: review passes, the pin is written, and different code installs.

The same shape shows up outside AI: deploy scripts that run git checkout $RELEASE_SHA, CI jobs that pull a pinned tool from an internal Git server, bootstrap scripts that vendor a dependency by commit. If the host allows hash-named branches and the script never reads back HEAD, the pin is a suggestion.

What I'd check on Monday

  • Agent versions. Claude Code at 2.1.179 or later, Codex at 0.146.0 or later. Run claude --version and codex --version everywhere, CI runners included.
  • Plugin inventory. /plugin marketplace list in Claude Code shows what each person has added. Flag any Git URL outside GitHub. Managed settings can also restrict which marketplaces people may add.
  • Copilot and Gemini CLI. Until Copilot ships a fix, I would limit it to trusted plugins hosted on GitHub. Gemini CLI belongs on a retirement plan.
  • Split your auto-update policy. Keep the agents themselves updating so vendor patches land. Be deliberate about plugin sources: Claude Code's docs say official Anthropic marketplaces auto-update by default, other third-party ones start with it off, and you can toggle it per marketplace.
  • Detection. Pareekh Jain, the analyst InfoWorld quotes, suggests examining machines running the vulnerable agents for unusual processes, network connections, plugin files, Git activity and credential use, and checking EDR, Git, CI/CD, cloud IAM and authentication logs.
  • Your own scripts. Search for git checkout with a SHA variable and add the read-back after it.

My take

Coding agents sit next to source code, credentials and CI/CD, a point Jain makes too. Installed plugins are easy to forget, which makes a silent auto-update path attractive. Teams can add controls around the edges, but the real fix belongs to the vendors, and two of the four have shipped it.

If you build agent tooling (I do, with Convoke), the rule is simple: resolve what you actually checked out and refuse to run anything that does not match the pin. Trust the result, not the request.

Sources

Primary references used for this post.

  1. air.security/blog-posts/plugin4shell (opens in a new tab)
  2. infoworld.com/article/4223907/a-zero-click-rce-flaw-in-ai-coding-agents-could-have-exposed-enterprise-systems.html (opens in a new tab)
  3. code.claude.com/docs/en/plugin-marketplaces (opens in a new tab)
  4. code.claude.com/docs/en/discover-plugins (opens in a new tab)

About the author

Umut Korkmaz

Lead Software Engineer

I'm a senior software engineer in Istanbul. Here I write about the tools and releases I've shipped to production myself, and what they taught me along the way.

Picked by shared topics.