T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/digest.sh:584
- Finding
- Arbitrary Python Code Execution Through Output-Path Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/digest.sh:584-698` **Vulnerability Type**: User-controlled shell value interpolated into dynamically generated Python source **Risk Level**: High ### Vulnerable Code ```bash compile_html() { log "Compiling HTML digest..." local md_file="${OUTPUT_DIR}/digest-${TODAY}.md" local html_file="${OUTPUT_DIR}/digest-${TODAY}.html" # If markdown file doesn't exist yet, compile it first [[ ! -f "$md_file" ]] && compile_markdown # Convert markdown to HTML with inline styles python3 -c " import re, html as html_mod # ... with open('${md_file}', 'r') as f: md = f.read() body = md_to_html(md) # ... with open('${html_file}', 'w') as f: f.write(html) " 2>/dev/null } ``` `OUTPUT_DIR` can be supplied through the command line: ```bash --output) OUTPUT_DIR="$2" shift 2 ;; ``` ### Technical Analysis The `--output` argument is controlled by the caller and becomes part of `md_file` and `html_file`. These paths are then interpolated directly into the source text passed to `python3 -c`. Shell quoting around `"${OUTPUT_DIR}"` does not protect the Python source. A path containing a single quote and a valid Python expression can terminate or alter the Python string literal. When HTML generation runs, Python interprets the resulting value as source code rather than as data. For example, a malicious output path can alter an expression such as: ```python with open('${md_file}', 'r') as f: ``` so that a Python function call executes while Python evaluates the argument passed to `open()`. Execution can occur before the subsequent file operation fails. This is a source-code injection vulnerability. It is distinct from ordinary shell command injection because the affected interpreter is Python. ### Attack Path 1. An attacker gains the ability to influence arguments used to invoke `digest.sh`, including through an automation wrapper or scheduled invocation. 2. The atta ...[truncated 1119 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Never interpolate paths or other external values into interpreter source code. Pass the paths as ordinary arguments: ```bash python3 - "$md_file" "$html_file" <<'PY' import sys md_file = sys.argv[1] html_file = sys.argv[2] with open(md_file, "r", encoding="utf-8") as source: md = source.read() # Convert the document here. with open(html_file, "w", encoding="utf-8") as destination: destination.write(html) PY ``` Additional hardening should include: 1. Validate `OUTPUT_FORMAT` before running the converter. 2. Resolve output paths with a canonicalization function and enforce an intended output root where appropriate. 3. Reject paths containing NUL characters or other unsupported path data. 4. Avoid `python3 -c` for scripts containing dynamic values. 5. Add regression tests using spaces, quotes, newlines, Unicode, and shell metacharacters in output paths. 6. Do not suppress all Python errors with `2>/dev/null`; return a controlled diagnostic without exposing secrets. ]]>
