T09 · Insecure Skill Coding Practices
- Location
scripts/marshal.py:1519- Finding
Shell Command Injection in Generated Runtime Hook
- Content
View full analysis
str: """Build a shell one-liner that checks Bash tool input against deny patterns.""" # The hook receives the tool input as JSON on stdin. # We build a Python one-liner that checks the command field. escaped = json.dumps(deny_patterns) return ( f"python3 -c \"" f"import sys,json,re; " f"data=json.load(sys.stdin); " f"cmd=data.get('command',''); " f"patterns={escaped}; " f"matches=[p for p in patterns if p.replace('*','') in cmd]; " f"sys.exit(2) if matches else sys.exit(0)" f"\"" ) ``` The generated command is subsequently presented as a Claude Code runtime hook: ```python hooks_config = { "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": _build_bash_hook_command(bash_deny_patterns), "timeout": 5, } ], }, ], } } ``` ### Technical Analysis The function serializes policy-derived values with `json.dumps()` and interpolates the result directly into a shell command enclosed in double quotes. JSON encoding is not equivalent to shell argument escaping. Values in `.marshal-policy.json`, including entries under `rules.commands.block` and `rules.commands.review`, therefore become part of shell source code. Shell substitutions and metacharacters embedded in those values may be interpreted when the generated hook is executed. For example, a value containing shell command substitution syntax can be expanded by the shell before Python receives the `-c` argum ...[truncated 1635 chars]- Remediation
View remediation
