T09 · Insecure Skill Coding Practices
- Location
SKILL.md:19- Finding
Arbitrary Python Code Execution Through Unsafe File-Path Interpolation
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 19–20 and 36–37
Vulnerability Type: Python code injection caused by unsafe shell-variable interpolation
Risk Level: HighVulnerable Code
bash OLD_SPEC="${1:?Usage: diff <old-spec> <new-spec>}" NEW_SPEC="${2:?Usage: diff <old-spec> <new-spec>}" python3 -c " # ... old = load_spec('$OLD_SPEC') new = load_spec('$NEW_SPEC') # ... " 2>/dev/nullTechnical Analysis
The first and second command-line arguments are assigned to
OLD_SPECandNEW_SPEC, then interpolated directly into the Python source passed topython3 -c.Shell quoting does not make these values safe Python string literals. An argument containing a single quote can terminate the Python string, inject Python expressions or statements, and comment out the remaining source on that line. The injected code executes before the OpenAPI comparison proceeds.
This is a source-code injection vulnerability rather than ordinary shell command injection: the shell constructs a Python program containing attacker-controlled text, and the Python interpreter executes that text. Redirecting standard error to
/dev/nullmay also conceal syntax errors or evidence of unsuccessful exploitation.Attack Path
- An attacker creates an API specification or filename containing Python syntax, or persuades the user or agent to invoke
diffusing such a path. - The malicious path is received as
$1or$2. - The shell substitutes it into
load_spec('$OLD_SPEC')orload_spec('$NEW_SPEC'). - A quote in the path escapes the intended Python string literal.
- The Python interpreter executes the injected code with the privileges of the user or agent running the skill.
For example, a path shaped like the following demonstrates the injection structure:
text x');__import__('os').system('id');#It would transform the affected statement into code equivalent to:
python old = load_spec('x');__import__('os').system('id');#') ...[truncated 825 chars]- An attacker creates an API specification or filename containing Python syntax, or persuades the user or agent to invoke
- Remediation
View remediation
Remediation Suggestions
Do not interpolate file paths or other untrusted values into executable Python source. Pass them as distinct command-line arguments and read them through
sys.argv.A hardened structure is:
bash OLD_SPEC="${1:?Usage: diff <old-spec> <new-spec>}" NEW_SPEC="${2:?Usage: diff <old-spec> <new-spec>}" python3 - "$OLD_SPEC" "$NEW_SPEC" <<'PY' import json import sys def load_spec(path): with open(path, encoding="utf-8") as f: if path.endswith((".yml", ".yaml")): try: import yaml return yaml.safe_load(f) except ImportError: print( "Warning: PyYAML not installed, trying JSON parse", file=sys.stderr, ) return json.load(f) return json.load(f) old = load_spec(sys.argv[1]) new = load_spec(sys.argv[2]) # Perform the comparison here. PYAdditional hardening measures:
- Keep the heredoc delimiter quoted (
<<'PY') so the shell cannot expand variables or command substitutions inside the Python program. - Pass every external value through
sys.argv, standard input, or another data-only channel. - Never construct Python source, shell commands, or expressions by concatenating user-controlled filenames.
- Validate that both paths refer to expected regular files and handle symbolic links according to the intended trust boundary.
- Report parsing failures explicitly rather than suppressing all standard-error output with
2>/dev/null. - Add regression tests using paths containing single quotes, double quotes, spaces, semicolons, command substitutions, newlines, and Python syntax to verify that filenames remain data rather than executable code.
- Keep the heredoc delimiter quoted (
