SHA Pinning Did Not Save the Plugin Install

Air Security's Plugin4Shell hits Claude Code, Codex, Copilot, and Gemini CLI. Two vendors patched. Copilot is disputed. AI commits still leak secrets at 2x.

A laptop on a wooden desk showing a terminal with a git checkout line, a USB key beside it, cool daylight, no logos, no readable UI text

The install command is now an identity incident.

Shattered.io’s September 23 recap of Plugin4Shell is the week’s AI-tools story that is not another benchmark. Air Security published the research on September 17. Or Nevo, Dor Granat, and Niv Hoffman are the names attached, via FourWeekMBA. By the 18th, The Hacker News and Help Net Security had the four-product list: Anthropic’s Claude Code, OpenAI’s Codex, GitHub Copilot, and Google’s Gemini CLI. Heise’s September 21 headline used the same four.

We already wrote that developers use these tools more than they trust them. This week is why the trust gap has a checkout hash in it.

What Plugin4Shell actually is

Call it a zero-click remote code execution bug in plugin install paths that were supposed to be saved by SHA pinning. The public mechanism, in the Shattered recap, is ugly in a small way: branch names that collide with commit SHAs, or a FETCH_HEAD trick against Gemini CLI. You need write access to a plugin repository and knowledge of which plugin a target has installed. After that, the victim does not have to click through a prompt.

That is not a drive-by on a random laptop. It is a supply-chain shape. If your team installs “the usual” Claude Code or Copilot plugins from repos a lot of people can push to, the write-access requirement is not a comfort.

Shattered is explicit that nobody has reported confirmed exploitation, a breach, or data loss. Treat that sentence as “not in the press,” not as “safe.” The write-up also says the technical detail is public enough that reproducing it does not require a research lab.

SHA pinning was the control people pointed at in design reviews. Plugin4Shell is the demonstration that a pin on paper is not the same as verifying the commit you actually checked out.

Four vendors, four clocks

As of September 23, Shattered’s table is the only status board I am willing to repeat:

  • Claude Code: patched, per secondary reporting. Exact fixed version not published.
  • Codex: patched, per secondary reporting. Exact fixed version not published.
  • Gemini CLI: one outlet reported the affected tool as deprecated. Patch status otherwise unclear.
  • GitHub Copilot: disputed. Air Security says Microsoft was notified and had not shipped a fix at disclosure. GitHub is reported to have said the platform was not affected because of existing mitigations. Those two sentences have not been reconciled in public.

Update Claude Code and Codex anyway. “Patched” without a version pin is a rotten advisory, but it is still a reason to stop living on last month’s binary. For Gemini CLI and Copilot plugin installs, the same recap says extra caution until each vendor publishes a clear note.

If your security chat is still asking whether Copilot is “in or out,” that is the accurate state. Do not write a Slack manifesto that GitHub is lying. Do not write one that Air Security is wrong. Write: disputed, so we do not auto-update plugins from shared repos until someone on our side has run git rev-parse HEAD against the hash we expected.

Claude Code’s recent product news was parallel agents. Cursor moved cloud agents into Cloudflare sandboxes. Those are capability stories. Plugin4Shell is what happens when capability includes “install this.”

The checkout you should run by hand

Shattered’s mitigation list is more useful than the brand fight.

Update Claude Code and Codex to whatever is latest this week.

Confirm the checked-out plugin commit with git rev-parse HEAD before you trust the install. Do not let the agent tell you the pin is fine.

Disable automatic plugin updates if the tool has a switch. Review diffs like you would a dependency bump.

Audit the repositories your plugins come from. Flag the ones where write access is a group of fifty.

Watch for default-branch games and for new branches named after commit hashes or FETCH_HEAD.

That last item is specific enough to be a hunting query. If you have an internal mirror of a plugin, look at branch names this afternoon.

Enterprise wrappers inherit this. If your company glued a private plugin onto Claude Code, Codex, Copilot, or Gemini CLI, you do not get to wait for the vendor blog. Procurement’s new question is not “do you SHA-pin.” It is “how do you verify plugin checkout integrity, and which version of that check is in the build we run.”

Secrets make the RCE expensive

The Hacker News piece on September 24 is the other half of the same week. GitGuardian’s 2026 State of Secrets Sprawl Report says commits identified as AI-assisted leak secrets at about twice the rate of human-written ones. The fastest-growing leaked credential categories, in that report, are now tied to AI services.

An agent can read a tree, edit files, write configs, and call out to other systems in the time a human reads one pull request. The secrets were designed for a world where a person pasted them. They were not designed for software that acts.

The same key ends up in .env, a CI variable, and a Jira ticket from last Tuesday’s failed deploy. Rotate the copy in git and the other copies still authenticate. Repository scanning is a start. It is not the incident.

If Plugin4Shell lands in an environment like that, you are not looking at a novelty RCE in a chat sidebar. You are looking at an agent that already had too many keys, plus a plugin checkout you did not actually pin.

Over-permissioned agent credentials are the boring version of this. If the coding agent can push, deploy, and read the secret store, a plugin bug is a production bug. If the agent can only write a branch and wait for CI, you still have a problem, just a smaller blast radius.

We already treated Devin’s plan and SWE-2 as different products. Treat plugins the same way. A marketplace install is not a sandboxed demo.

The FETCH_HEAD and hash-named-branch tricks sound like git trivia until you remember how plugin installers actually clone. They are not doing a careful human review of git log. They are resolving a pin, checking out a ref, and then executing whatever the plugin’s install hook says is allowed. If an attacker with write access can make the resolver land on a different commit than the SHA you thought you pinned, the rest of your model-card talk does not matter. The agent will run the hook with the same privileges it uses to edit your tree.

That is why Shattered’s “confirm with git rev-parse HEAD” line is not pedantry. It is the control that does not live inside the agent’s own story about pinning. If your internal plugin catalog cannot show a human-readable expected hash next to a clone URL, you do not have a catalog. You have a wiki page of install commands.

On the secrets side, the GitGuardian 2x figure should change how you rotate, not just how you lecture. If AI-assisted commits are twice as likely to leak, then the agent-written config is the first place you look after Plugin4Shell, not the last. Search for cloud keys, model-provider keys, and the bot tokens people paste so the agent can open PRs. Rotate the copies in CI and in the ticket tracker, not only the copy that git-secrets found. The Hacker News piece is explicit that a secret which still authenticates outside the repo is not rotated.

If you cannot rotate everywhere the same day, cut the agent’s scopes first. Read-only on the secret store. No production deploy keys in the plugin host. No shared GitHub app that can push to every internal plugin repo. Blast radius is a privilege conversation that does not require waiting for Microsoft and Google to agree on the Copilot row of the table.

What not to do in Slack this week

Do not paste a “we banned AI tools” memo because four vendors had a plugin story. The tools are already in the repo history. GitGuardian’s 2x number is about commits that happened.

Do not take GitHub’s “not affected” line as a close, and do not take Air Security’s “no fix” line as a close. Put both in the ticket.

Do not confuse SHA pinning with git rev-parse. The whole point of Plugin4Shell is that the pin was the story and the checkout was the hole.

Do not dump more API keys into .env files so the agent can “just run the stack.” That is how the secrets report gets its next chapter.

SD Times published a September 25 column that adoption is settled and trust is the product. Fine. Trust, this week, is a hash.

The install is part of the model

I care less about which of the four logos looks worse on a slide. I care that plugin systems were sold as extensions and implemented as remote code with a pin ceremony.

If you run a platform team, freeze auto-updates, inventory the plugin repos, and make “expected SHA” a required field in the internal catalog. If you are a staff engineer who installed three Copilot plugins from memory, go look at HEAD before you open the next ticket.

The research disclosure is nine days old as of this article. Two vendors, we are told, patched. One deprecation rumor. One dispute. That is not a completed incident. It is a week where the install command earned a line in the threat model next to the prompt.

If you own an internal Claude Code or Codex plugin, treat “patched” as a homework assignment, not a status emoji. Find the release notes. If they do not name a version, pin the binary you actually deployed and write the date you pulled it. Secondary reporting is how we got the table. It is not how you close a Jira ticket.

Gemini CLI’s deprecation rumor is the row people will screenshot. Do not uninstall a tool because one outlet used the word. Do not keep auto-updating it either. Freeze the install, read Google’s own advisory if it exists this week, and if you cannot find one, assume the FETCH_HEAD trick is still in play.

Copilot stays in the disputed cell. Security can live with a disputed cell. Engineering cannot live with an install hook that nobody hashed. Until GitHub or Microsoft publishes something you can cite, the local rule is: no new Copilot plugins from repos outside a short allowlist, and every allowlisted repo has two-person write access or it gets dropped.