{"slug": "plugin4shell-your-ai-coding-agent-s-pinned-dependency-was-never-actually-pinned", "title": "Plugin4Shell: Your AI Coding Agent's \"Pinned\" Dependency Was Never Actually Pinned", "summary": "Security researchers at AIR disclosed Plugin4Shell, described as the first supply chain vulnerability in the AI agent ecosystem, showing that Claude Code, Codex, GitHub Copilot, and Gemini CLI check out pinned plugin commits without verifying the working tree actually matches the pinned SHA. Attackers can exploit branch name collisions or FETCH_HEAD confusion to substitute arbitrary code with zero user interaction. Anthropic and OpenAI have patched their agents, while Microsoft's Copilot remains unpatched and Google deprecated Gemini CLI without a fix, leaving users permanently exposed.", "body_md": "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.\n\nThey'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.\n\nEvery one of these agents checks out the pinned commit — and then never verifies the checkout actually landed there.\n\nThat'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.\n\n**Vector 1 — branch name collision (Claude Code, Codex, GitHub Copilot)**\n\nThese three run something functionally equivalent to:\n\n```\ngit clone <plugin repo> ./\ngit checkout aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n```\n\nGit 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.\n\n**Vector 2 — FETCH_HEAD confusion (Gemini CLI)**\n\n```\ngit clone --depth 1 <plugin repo> ./\ngit fetch origin 41d0bc0a4aeb2fbf797dacea39e876d98c95024b\ngit checkout FETCH_HEAD\n```\n\nName 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.\n\nThis is the part that should actually worry you. The attack chain is:\n\n`aaa...`. It passes review. People install it.` bbb...`. Still clean. Trust builds.\nPrior 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.\n\n| Vendor | Status | Version | \n|---|---|---|\n| Anthropic (Claude Code) | **Patched** | 2.1.179+ | \n| OpenAI (Codex) | **Patched** | 0.146.0+ | \n| Microsoft (Copilot) | **Unpatched** | No fix shipped | \n| Google (Gemini CLI) | **Deprecated, unpatched** | Users pointed to Antigravity CLI instead | \n\nRead 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.\n\nAnd 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.\n\nAIR's actual recommendation is embarrassingly simple — verify what you checked out:\n\n```\ntest \"$(git rev-parse HEAD)\" = \"<pinned-sha>\" || abort\n```\n\nOne 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.\n\nThe 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.", "url": "https://wpnews.pro/news/plugin4shell-your-ai-coding-agent-s-pinned-dependency-was-never-actually-pinned", "canonical_source": "https://dev.to/ashraf_chowdury09/plugin4shell-your-ai-coding-agents-pinned-dependency-was-never-actually-pinned-32el", "published_at": "2026-09-19 09:02:21+00:00", "updated_at": "2026-09-19 09:24:35.908141+00:00", "lang": "en", "topics": ["ai-agents", "ai-safety", "developer-tools", "ai-tools"], "entities": ["AIR", "Claude Code", "Anthropic", "OpenAI", "Codex", "GitHub Copilot", "Microsoft", "Gemini CLI"], "alternates": {"html": "https://wpnews.pro/news/plugin4shell-your-ai-coding-agent-s-pinned-dependency-was-never-actually-pinned", "markdown": "https://wpnews.pro/news/plugin4shell-your-ai-coding-agent-s-pinned-dependency-was-never-actually-pinned.md", "text": "https://wpnews.pro/news/plugin4shell-your-ai-coding-agent-s-pinned-dependency-was-never-actually-pinned.txt", "jsonld": "https://wpnews.pro/news/plugin4shell-your-ai-coding-agent-s-pinned-dependency-was-never-actually-pinned.jsonld"}}