T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/marshal.py:1519
- Finding
- Shell Command Injection in Generated Runtime Hook<![CDATA[ ## Vulnerability Details **File Location**: `scripts/marshal.py:1519-1531` **Vulnerability Type**: Shell command injection through policy-controlled hook generation **Risk Level**: High ### Vulnerable Code ```python def _build_bash_hook_command(deny_patterns: list[str]) -> 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
- <![CDATA[ ## Remediation Suggestions 1. Do not place policy-derived content inside shell source code. 2. Generate a standalone Python hook script containing fixed, reviewed logic. 3. Have that script load and validate `.marshal-policy.json` as data at runtime. 4. Invoke the hook using a fixed command and separately supplied arguments rather than a shell-composed one-liner. 5. If command serialization is unavoidable, use platform-appropriate argument handling and avoid invoking through a shell. On POSIX systems, `shlex.quote()` may be part of the defense, but direct argument arrays are preferable. 6. Validate the policy against a strict schema, including type, length, and allowed-character constraints. 7. Treat malformed policy data as a fail-closed configuration error and provide a clear diagnostic. 8. Add security tests covering quotes, backslashes, command substitutions, semicolons, newlines, and platform-specific shell metacharacters. ]]>
