AI编码
dev.to ·
@Ashraf
Plugin4Shell:你的 AI 编码 agent 所谓“已固定”的依赖其实从未真正固定
文章称安全研究机构 AIR 发现,Claude Code、Codex、GitHub Copilot 和 Gemini CLI 四个主流 AI 编码 agent 在检出被固定到某 commit SHA 的插件后,并未验证检出结果确实落在该 commit 上,研究者将其命名为 Plugin4Shell,并称这是 AI agent 生态的首个供应链漏洞,零点击、无需用户交互即可触发。文章为二手转述,未附原始报告或复现细节。
AI推荐理由 文章转述安全研究机构 AIR 的发现:Claude Code、Codex、GitHub Copilot 和 Gemini CLI 四个主流 AI 编码 agent 在检出被 pin 到某个 commit SHA 的插件后,并未验证检出结果确实落在该 commit 上,研究者称之为 Plugin4Shell,并称其为 AI agent 生态的首个供应链漏洞,零点击即可触发。对使用这些工具的开发者有直接安全警示价值;但内容为二手转述,未提供原始报告链接或复现细节,需保留不确定性。
80
正文
Your SHA pin is a suggestion, not a lock
You pin a plugin to a commit SHA because you did the review, you trust that exact code, and you never want it to silently change. That's the entire point of pinning. Last week, security researchers at AIR proved that four of the biggest AI coding agents — Claude Code, Codex, GitHub Copilot, and Gemini CLI — treat that pin as a polite suggestion instead of a hard constraint.
They're calling it Plugin4Shell, and the researchers are blunt about what it is: "the first supply chain vulnerability of the AI agent ecosystem." Zero-click, no user interaction required, and it hits tools that a huge chunk of the industry now runs with elevated trust and shell access.
The bug, in one sentence
Every one of these agents checks out the pinned commit — and then never verifies the checkout actually landed there.
That's it. That's the whole flaw. Verification theater: the pin looks honored, the hash is right there in the config, and the working tree is running something else entirely.
How you actually get owned
Vector 1 — branch name collision (Claude Code, Codex, GitHub Copilot)
These three run something functionally equivalent to:
git clone ./
git checkout aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Enter fullscreen mode
Exit fullscreen mode
Git resolves refs before it resolves raw object IDs. So if an attacker who controls the plugin repo creates a branch named exactly aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa and makes it the default, git checkout happily hands you the branch tip instead of the commit you pinned. Same hash string, completely different code.
Vector 2 — FETCH_HEAD confusion (Gemini CLI)
git clone --depth 1 ./
git fetch origin 41d0bc0a4aeb2fbf797dacea39e876d98c95024b
git checkout FETCH_HEAD
Enter fullscreen mode
Exit fullscreen mode
Name a branch FETCH_HEAD, make it default, and the checkout resolves to that branch instead of the commit you just fetched. Different mechanism, same failure: nobody diffed what actually ended up in the working directory against what was requested.
Why "zero-click" isn't marketing spin here
This is the part that should actually worry you. The attack chain is:
Attacker publishes a clean, useful plugin pinned at commit aaa.... It passes review. People install it.
Attacker ships a routine version bump to bbb.... Still clean. Trust builds.
Attacker creates a branch literally named bbb... on their own repo, with malicious code as the default branch.
Your agent's background auto-updater — on by default in Claude Code and Codex — re-runs the checkout logic against the new pin.
You now have attacker-controlled code executing with your coding agent's permissions. You did nothing. You clicked nothing. You were probably in a meeting.
Prior AIR research already found 925 compromised "skills" reaching 134,000 agents through similar takeover patterns — this isn't a hypothetical, it's a repeatable playbook that just got a much bigger blast radius.
Patch status: pick your poison
Vendor
Status
Version
Anthropic (Claude Code)
Patched
2.1.179+
OpenAI (Codex)
Patched
0.146.0+
Microsoft (Copilot)
Unpatched
No fix shipped
Google (Gemini CLI)
Deprecated, unpatched
Users pointed to Antigravity CLI instead
Read that last row again. Google didn't patch it. Google killed the product and told everyone to migrate. If you have Gemini CLI installed anywhere, it is permanently exposed — there is no version number that fixes this, because there won't be one.
And if you're on Copilot: nearly 90% of Fortune 500 companies use it. That's not a niche exposure, that's most of corporate engineering running an agent with a known, public, unpatched RCE path.
The fix, and why it has to live in the agent
AIR's actual recommendation is embarrassingly simple — verify what you checked out:
test "$(git rev-parse HEAD)" = "" || abort
Enter fullscreen mode
Exit fullscreen mode
One line. Run it after every checkout, before you trust anything in that working tree. The reason this has to be enforced by the agent itself — not the marketplace, not some registry-side scan — is that marketplace controls only see what was published. They can't see what ends up on your disk after your local git client resolves refs. The verification gap is entirely client-side, so the fix has to be too.
What to actually do this week
Check your installed agent versions right now. If you're running Claude Code or Codex, confirm you're on 2.1.179 / 0.146.0 or later.
If you're on Copilot or Gemini CLI, assume you're exposed. Disable plugin auto-update if you can, and manually audit what's actually checked out versus what your lockfile claims.
Add the verify-after-checkout line yourself to any internal tooling that does pinned git checkouts — this pattern isn't unique to AI agents, it's a general git footgun that just got a viral name.
Don't assume "SHA pinned" means "SHA verified" anywhere in your stack going forward. This bug exists because everyone assumed git's checkout semantics matched their mental model. They don't.
The uncomfortable meta-point: these agents run with real filesystem access, real shell access, and increasingly real production credentials. We spent years teaching developers to pin dependencies as a security baseline. Turns out the pin was never being checked. Audit your agent's supply chain like you'd audit npm's — because apparently you have to.