AI DevelopmentAnalysis6 min readPublished September 20, 2026

4 agents · 2 patched · 1 flaw class · git's own rule · what a pin promises

Your Agent Pinned a Plugin to a Hash. It Got the Branch.

A September 17 advisory shows four coding agents resolving a pinned commit to a same-named branch. How git decides, which versions fix it and what a pin means.

DA
Digital Applied Team
Research and practical guidance
Editorial dateSeptember 20, 2026
Advisory publishedSeptember 17, 2026

A pin is a promise: install exactly this commit, nothing else. On September 17, 2026 three researchers at AIR Security published an advisory showing that four coding agents broke that promise. Each pinned a plugin to a commit hash and none checked that the commit it pinned was the one that landed; in three of the four, a repository owner who created a branch named after the hash got the branch checked out instead. The owner could then push whatever they liked to the branch, and every agent that auto-updated its plugins would install it without a prompt.

Two of the four had fixed it before the advisory went public. The rest of this post is about the part that outlives the patch: what git actually does when a name could be either a branch or a commit, how each extension system you rely on verifies the code it installs, and five questions to ask about your own setup. Every claim about the flaw is the researchers'; every claim about git is from git's own documentation.

Key takeaways
  1. 01
    git prefers a branch over a commit when a name could be both.git's checkout documentation says a name that is a valid ref under refs/heads/ is treated as a branch, and only otherwise as a commit. An attacker who names a branch after a hash gets the branch.
  2. 02
    Claude Code 2.1.179 and Codex 0.146.0 carry the fix.Both shipped months before the September 17 disclosure. Copilot had no fix at disclosure, and Google is not patching the retired Gemini CLI, according to the advisory.
  3. 03
    No CVE and no exploitation in the wild are recorded.The advisory does not cite a CVE identifier or an observed attack. 'Zero-click' is the researchers' severity term, not evidence of use.
  4. 04
    The question to ask any extension system is what it checks after download.A hash in a manifest proves nothing unless the installer compares it with what arrived. Signed packages, provenance attestations and a checked-out commit compared against the pin are three different answers.

01The advisoryWhat the researchers found

The advisory, by Or Nevo, Dor Granat and Niv Hoffman, dates the discovery to May 2026, coordinated disclosure to the vendors to June, and publication to September 17. The name they gave the class is Plugin4Shell. The mechanism as they describe it is short: a plugin marketplace records a commit hash, the agent runs a checkout of that hash, and if the repository has a branch whose name is the same string, git checks out the branch instead. In the researchers' words, "when a name is both a valid ref and an object id, git prefers the ref…"

Three of the four agents shared that variant. Gemini CLI's was different: a repository whose default branch is named FETCH_HEAD overrides the commit the agent had just fetched. The researchers classify the result as a zero-click remote code execution, meaning in their description that no user interaction is needed once the agent updates. That is their severity call and we report it as such. The advisory does not cite a CVE and does not report any exploitation in the wild, and we found none elsewhere.

Why this is a class, not an incident

The four products are the instance. The class is any installer that accepts a git reference as a pin and hands it to a checkout without checking what came back. That covers plugin marketplaces, skill packs, MCP server installers and home-grown deploy scripts alike, which is why the second half of this post is a census of verification rather than a story about four vendors.

02The mechanismHow git decides: the transferable part

None of this is a git bug. It is documented behaviour. The git checkout reference describes the branch argument as follows: if the name, when prefixed with refs/heads/, is a valid ref, then that branch is checked out; otherwise, if it names a valid commit, HEAD becomes detached at that commit. A forty-character hexadecimal string is a perfectly legal branch name. So the moment such a branch exists, the first rule fires and the second is never reached.

git does try to warn. The configuration reference documents a setting, on by default, under which git will warn you when a ref name you passed is ambiguous and might match several refs. A warning on standard error is exactly the kind of signal an automated installer discards. The fix is the same in every product: after the checkout, read the commit that HEAD points at and refuse if it differs from the pin, or fetch the object by hash directly and never pass the string through name resolution at all.

If true, Git will warn you if the ref name you passed it is ambiguous and might match multiple refs in the repository. True by default.git-config documentation, core.warnAmbiguousRefs, read September 22, 2026

03The fourThe four agents and their patch state

Patch states below are those the advisory records, cross-checked against each vendor's own release record where one exists. The Claude Code version was dated from the npm registry. The Codex version was dated from its GitHub release. Neither vendor's notes name the advisory.

AIR Security advisory of September 17, 2026; npm registry; the Codex release page; Google's May 19, 2026 retirement post. Patch states as of the advisory, not re-verified after September 20.
AgentState at disclosureVersion and dateWhat a user does
Claude CodePatched before disclosure2.1.179, published to npm June 16, 2026; the researchers confirmed the fix June 17. The public changelog entry for that version does not mention it.Run the CLI with its version flag; update if below 2.1.179.
OpenAI CodexPatched before disclosure0.146.0, released July 29, 2026, whose notes include the line 'Verify Git plugin SHA checkouts'; the researchers verified it August 12.Run the CLI with its version flag; update if below 0.146.0.
GitHub CopilotNo fix shipped at disclosureThe advisory records no patched version. We read no GitHub statement dated on or before September 20.Treat every plugin pin as a branch reference until GitHub documents a fix.
Gemini CLINo patch plannedThe advisory's timeline records Google confirming on August 4, 2026 that no fix will ship because the Gemini CLI is deprecated, and advising migration to Antigravity. The variant differs: a default branch named FETCH_HEAD overrides the fetched commit.Google's own May 19 announcement points users to Antigravity; Code Assist licence holders keep access.

One detail from Claude Code's current documentation is worth knowing if you write a marketplace. Its plugin marketplace reference says that when a git-based plugin source sets both a ref and a sha, the sha is the effective pin and Claude Code fetches and checks out the pinned commit directly. The marketplace catalog itself, by contrast, supports a ref but not a sha, so the list of plugins can still move even when each plugin cannot. Version 2.1.277, released September 18, also fixed the installed-plugins record dropping the commit for official-marketplace plugins, which is the record a user would read to know what they actually have.

04The censusWhat other extension systems verify

Agents install code from more places than plugin marketplaces. Each system below is classified by what its own documentation says the installer checks after download. One row is marked unverified, because we read no first-party statement in this pass for either system it names; they are named so that you check them, not skipped so that you forget.

Verified
VS Code extensions
Signature on install

The Visual Studio Marketplace signs every extension at publish time and VS Code verifies that signature on install to check integrity and source. Since release 1.97 a first install from a third-party publisher also asks you to confirm you trust the publisher.

Signed package
Verified
npm packages
Provenance and staging

npm's provenance statements record where a package was built and who published it, verifiable by consumers. Since September 18, a stage-only token can submit a version that a maintainer must approve with two-factor authentication before it publishes.

Attested build
Verified
Claude Code plugin sources
Commit pin, archive digest

A git source with a sha is fetched and checked out at that commit; an archive source may carry a SHA-256 digest. The marketplace catalog itself pins by ref only.

Pinned fetch
Unverified
MCP servers and skill packs
Not documented in this pass

Most MCP servers are started from a package name or a git URL by whatever runner the config names, and skill packs are folders copied from a repository. We read no first-party verification statement for either; treat both as unpinned until you have one.

Check yours

The pattern across the verified rows is that the check happens on the artifact, not the name: a signature over the package, an attestation over the build, a commit compared against the pin. The unverified row is the one where a name is all there is. Our earlier posts on skill packs as a package ecosystem and on the lessons of an earlier plugin incident cover those two categories in depth.

05The checkFive questions for your own setup

You do not need to reproduce anything to know where you stand. Five questions, each answerable from a version number or a configuration file, cover the class.

What version am I on?Claude Code at or above 2.1.179; Codex at or above 0.146.0
Version flag
Does my agent update plugins without asking?Auto-update is the step that turns a bad branch into an install
Settings
Are my plugins pinned by full commit hash or by ref?A tag or branch name is a pointer the owner controls
Manifest
Does the installer compare what it checked out with the pin?The fix in both patched agents; ask your vendor if undocumented
Vendor docs
Can I read which commit is actually installed?Claude Code records it in installed_plugins.json; 2.1.277 fixed missing and stale commits there
Local record

The credential side of the same question, what a token lets an automated publisher do, is the subject of our companion post on stage-only npm tokens. And what still does not port when you move plugins between agents is in our plugins portability post. If you run agents that install their own extensions, our AI transformation service includes this check as part of an agent security review.

06Next stepA pin is only as good as the check after the download

Put it into practice

Run the five questions against every agent that installs code for you

Update Claude Code and Codex if you are below the fixed versions. Stop letting Copilot pull plugin updates unattended until GitHub documents a fix, and move off the retired Gemini CLI. Then open every manifest that pins a git source and replace any ref with a full commit hash, knowing that even that is only a promise until the installer checks it.

Digital Applied

Know what your agents install, and what they verify.

We inventory every plugin, skill pack and MCP server your agents pull in, check how each one is pinned and verified, and fix the ones that only look pinned.

Extension inventoryPin and verification auditUpdate policy
Your next project

Start with the inventory

  • Every source your agents install from
  • How each one is pinned
  • What the installer checks after download
Questions and answers

Applying this post

No. git's checkout documentation says a name that is a valid branch is checked out as a branch, and only otherwise treated as a commit. The flaw is in installers that pass a hash through that rule without checking what came back.
Digital Applied newsletter

Deep dives on AI, marketing and development.

Practical guides and fresh insights by email. No recycled takes.

Related dispatches

Continue reading

AI Development

Your CI Agent Can Publish Packages. Narrow That Token Now

npm added stage-only tokens on September 18, 2026 and targets January 2027 to end direct publishing by bypass-2FA tokens. Who moves to what, and what stays.

September 20, 2026 · 5 minRead
AI Development

Which AI Coding Tools Read Which Instruction File? 9 Tools

A matrix from vendor documentation: which filenames nine coding agents read, what wins when several exist, nested files, size caps and what is not documented.

September 20, 2026 · 7 minRead
AI Development

Does Your AI Agent Act as You or as Itself? 20 Products

A census of 20 AI agents by the identity each acts under: 11 get their own account or token, 9 reuse your login. What it means for revocation and blast radius.

September 19, 2026 · 8 minRead
AI Development

A Hijacked AI Assistant Login Can Reach Your Connected Apps

Researchers took over OpenAI staff ChatGPT accounts via a forum image bug and an SSO flaw, then reached internal repos via Codex. A checklist for connector use.

September 18, 2026 · 8 minRead
AI Development

AI-Built Forms: Keep User Input When Submission Fails

Test AI-built forms beyond a successful submit. Preserve valid input, explain errors and distinguish a rejected request from an outcome still unknown.

September 6, 2026 · 4 minRead
AI Development

Small AI-Built Tools: Set the Boundary Before You Build

Scope a small AI-built utility around clear inputs, outputs and limits. Decide what it should own, reject and preserve before it grows into a system.

September 6, 2026 · 4 minRead