The Claude Code Changelog
All Episodes

Claude Code Plugin Security: Fixing Shell Injection

This episode breaks down the breaking changes in Claude Code 2.1.207, including why raw template strings in custom hooks were removed and how shell-injection attacks can happen through plugin configs.

It also covers the safer migration paths with exec-form arrays and CLAUDE_PLUGIN_OPTION_ environment variables, plus the new decision to ignore local repository config files for plugin resolution.


Chapter 1

Securing Your Claude Code Plugins against Shell Injection

Lachlan Reed

So, I- I was hacking away on a custom git-hook plugin for Claude Code last night in my shed, right? And I- I nearly gave myself a massive heart attack because my local build just completely refused to run. I'm talking flat-out dead in the water. Turns out, version 2.1.207 dropped, and it absolutely guts how we process user configurations. It's a massive breaking change, fair dinkum.

James Turner

Oh yeah, the 2.1.207 release is huge, Lachlan! It completely deprecates raw template strings in custom hooks. Why? Because of shell-injection vulnerabilities. If you are concatenating user inputs directly into a shell command inside your plugin manifest, an attacker could inject arbitrary commands—like, uh, rm -rf—just by manipulating a configuration option. The tool is brought to you by Jellypod AI, by the way, but seriously, this security fix is a massive deal for anyone building Claude Code extensions.

Lachlan Reed

Right, so it's like putting raw user input straight into a SQL query without escaping it. You're just asking for trouble. A real, uh, snake in the grass scenario. So if the raw template strings are out, how do we actually pass those config options now without leaving the back door wide open?

James Turner

You have two secure paths now. First, you can migrate to structured, exec-form arrays in your JSON manifests. Instead of writing a single string like "command": "echo $USER_INPUT", you have to define it as an array of arguments, like ["echo", "user_input"]. This bypasses the shell interpreter entirely, so the arguments are passed directly to the executable. No shell evaluation, no injection vector. It's clean.

Lachlan Reed

Okay, so the exec-form array is the gold standard. But what if we actually need those env vars? What's the go there?

James Turner

That's the second path. Claude Code now automatically exports your plugin configuration options as environment variables prefixed with CLAUDE_PLUGIN_OPTION_. So if you have a config option named api_key, it's exposed in the execution environment as CLAUDE_PLUGIN_OPTION_API_KEY. You just read it directly from the environment inside your script. It's completely sandboxed and safe from shell parsing exploits.

Lachlan Reed

Right, that makes heaps of sense. So you're not stitching strings together like a bad patchwork quilt. But wait, there was something else in the release notes that caught my eye. They're changing how the tool looks up these configuration files, yeah? Something about local repository settings being ignored?

James Turner

Yes, exactly. To prevent a malicious repository from committing a rogue .clauderc or local config file that automatically runs exploit payloads when a developer opens the project, Claude Code now ignores local repository settings files for plugin configuration resolution. It forces the resolution to happen at the global user level or via explicitly passed environment configurations. It stops drive-by attacks on developers cloning untrusted repos.

Lachlan Reed

Well, that's a massive win for peace of mind. Though I'll admit, while they were busy tightening the security screws, they did throw in some nice quality-of-life updates. The autocomplete for /cd in the terminal is finally working, and they added credential timeout guards for AWS Bedrock, which is handy if your session tokens keep dying mid-run.

James Turner

Absolutely, the Bedrock timeout guard prevents the CLI from hanging indefinitely when your AWS STS tokens expire. It's a solid, robust release. If you're building plugins, migrate those manifests to exec-form arrays or start reading CLAUDE_PLUGIN_OPTION_ env vars right away.