Plugin4Shell: A Pinned Plugin Is Not a Verified Plugin
A SHA pin that nobody checks is a label, not a control. Plugin4Shell showed that four major coding agents never checked.
Key takeaways
- A SHA pin that nobody checks is a label, not a control.
- Plugin4Shell showed that four major coding agents never checked.
On this page
Plugin4Shell is a zero-click remote code execution bug in the plugin marketplaces of Claude Code, OpenAI Codex, GitHub Copilot and Gemini CLI, disclosed by Air Security. The agents checked out the commit a marketplace had pinned, but never confirmed the working tree actually landed on it. Our verdict: a SHA pin that nobody verifies is a label, not a control. Upgrade Claude Code and Codex now, treat Copilot and Gemini CLI plugins as untrusted until proven otherwise, and add your own verification step anywhere you vendor plugins.
What actually broke#
Plugin marketplaces sell a simple promise. A reviewer looked at plugin code at a specific commit, the marketplace records that SHA, and every install gets exactly those bytes. Pinning to a full commit hash is the strongest reference git offers, which is why security guidance has pushed it for years.
Air Security's write-up shows where the promise leaked. The agent runs a checkout of the pinned reference and moves on. It never resolves what is really in the working tree afterward and compares it to the pin. In the main variant, git's own ref resolution does the damage: a branch named after the pinned hash can win over the commit it is meant to stand for, so the checkout succeeds, the pin looks honored, and different code lands on disk. A second variant against Gemini CLI abuses the same missing check on a fetched commit.
The code that lands then runs inside an agent that already has your shell, your files and your tokens. That is why this is zero-click: installing or updating the plugin is the whole attack.
Two ways an attacker gets there#
Both paths need control of the plugin's repository, and both are ordinary supply-chain moves rather than exotic ones.
The post-review swap: an attacker submits a clean plugin, waits for it to pass review and get pinned, then changes the repository afterward. The review was real. The pin was real. Neither bound the bytes you received.
The hijacked author: an attacker steals a maintainer's credentials or token, pushes to a legitimate plugin's repository, and every agent with that plugin installed pulls the result. This is the same shape as the incidents we covered in npm supply chain CI hardening, moved from your build to your laptop.
Only a check made at install time, on the machine doing the install, closes them.
Where each vendor stands#
The vendors were notified before public disclosure, and the outcomes split unevenly.
Claude Code: fixed in 2.1.179. Codex: fixed in 0.146.0. GitHub Copilot: no patch had shipped as of the disclosure. Gemini CLI: Google deprecated the product and does not plan a fix.
Read that last pair carefully. If your team still runs Copilot's plugin mechanism or Gemini CLI, there is currently no vendor-side remedy, so the controls below are your only defense. Do not wait for a patch that may not come. Gemini CLI users in particular should treat migration as the fix, not a workaround.
The verification we would put in your setup#
The vendor fix is conceptually one step: after checkout, resolve the commit actually in the working tree and abort unless it equals the pinned SHA. You can do the same in any vendored plugin directory, in a pre-commit hook, or in a CI job that guards your team's shared agent configuration.
#!/usr/bin/env bash
# verify-plugin-pin.sh <plugin-dir> <pinned-sha>
set -euo pipefail
dir="$1"; pin="$2"
actual="$(git -C "$dir" rev-parse HEAD)"
if [ "$actual" != "$pin" ]; then
echo "FAIL: $dir is at $actual, pinned $pin" >&2
exit 1
fi
# A ref named like the pin can shadow the commit; refuse it outright.
if git -C "$dir" rev-parse --verify --quiet "refs/heads/$pin" >/dev/null; then
echo "FAIL: branch named after the pin exists in $dir" >&2
exit 1
fi
# Uncommitted changes mean the tree is not the pinned content.
git -C "$dir" diff --quiet HEAD -- || { echo "FAIL: dirty tree" >&2; exit 1; }
echo "OK: $dir == $pin"
Run it as $ ./verify-plugin-pin.sh ./plugins/my-plugin 4f9c2a1e... (full 40-character SHA, never an abbreviation). The point is that the comparison happens on your side, using values you wrote down, against the directory that will actually execute. For the wider habit of treating agent tooling as an untrusted dependency, see our AI agent security guide.
What to do this week#
- Upgrade: Claude Code to 2.1.179 or later, Codex to 0.146.0 or later. Check with
$ claude --versionand$ codex --version. - Audit installed plugins: list every plugin on every developer machine and CI runner, note its source repository, and delete anything nobody can justify. Plugins installed before your upgrade were fetched by the vulnerable code, so re-verify them.
- Turn off plugin auto-update: an update is a fresh chance to pull swapped code. Update on purpose, from a reviewed change, not on a timer.
- Allowlist marketplaces: permit one or two sources your security team has looked at, and block the rest at the configuration level where the agent supports it.
- Contain the agent: run it in a sandboxed container with scoped, short-lived credentials, no ambient cloud keys and restricted egress. A compromised plugin then reaches a disposable box instead of your laptop. Our post on running AI CLI agents in CI pipelines covers the container and token scoping in detail.
The last item matters most for Copilot and Gemini CLI, where the patch story is weakest.
The decision, concretely#
- Are you on Claude Code or Codex? Upgrade to 2.1.179 or 0.146.0 today, then re-verify every plugin installed before the upgrade.
- Are you on Copilot or Gemini CLI plugins? Assume exposure. Disable third-party plugins, and plan a move off Gemini CLI since it will not be fixed.
- Do you vendor plugins into your own repos? Add the pin verification script to CI so a mismatch fails the build.
- Do agents run with your real credentials? Move them into sandboxed containers with scoped tokens before you install another plugin.
The call we'd make#
Upgrade the two patched agents, freeze plugin updates, and put the verification script in front of anything you vendor. Then move agents into containers with scoped credentials, because the next bypass will not announce itself with a name. A pin is a claim about what you will get. Only a comparison run on your own machine turns that claim into a control.
Get the DevOps Troubleshooting Cheat Sheet
Subscribe and get our free one-page reference for the errors that eat an afternoon — CrashLoopBackOff, OOMKilled, Terraform state locks, and more — plus new guides as we publish them.
AWS Lost a Region for Good: Multi-AZ Is Not Disaster Recovery
AWS says it cannot restore data held only in Bahrain (me-south-1) or in one UAE zone. Multi-AZ gave availability, not recovery, and only cross-region copies survived.
EU CRA Article 14: What DevOps Teams Should Wire In Now
Since 11 September 2026 the 24-hour clock for actively exploited vulnerabilities is live. The owner, the decision path, the SBOM lookup, and the runbook you need this month.
More from AI
Explore more articles in this category
AI Agents and Kubernetes Remediation: Write Access Is the Easy Part
Handing an agent kubectl is a five-minute job. Proving the fix worked and did no harm is the real work, and it belongs in the wrapper, not the prompt.
Copilot's September Bill Cliff: Included AI Credits Just Dropped
The June to August promotion ended on September 1. Included Copilot credits fell 37% on Business and 44% on Enterprise while seat prices stayed flat. Here is the arithmetic and the controls.
AI CLI Agents in CI: Claude Code vs Codex CLI vs Gemini CLI
Running a coding agent on a laptop is a preference. Running one in a pipeline is an architecture decision about credentials, sandboxing, and non-interactive failure.
You might have missed
Evergreen posts worth revisiting.