T09 · Insecure Skill Coding Practices
- Location
audit.sh:51- Finding
Arbitrary Python Code Execution Through Unsafely Interpolated File Paths
- Content
View full analysis
Vulnerability Details
File Location:
audit.sh:51-59
Vulnerability Type: Python code injection through shell-to-Python string interpolation
Risk Level: CriticalComplete Code Snippet
bash ALLOWLISTED_SKILLS=$(python3 -c " import json,sys try: data = json.load(open('$ALLOWLIST_FILE')) for s in data.get('skills', []): print(s.get('name', '')) except: pass " 2>/dev/null || true)The same unsafe construction is repeated for attacker-influenced target paths:
bash if python3 -c " import json,sys try: d = json.load(open('$pkg_file')) scripts = d.get('scripts', {}) if any(k in scripts for k in ['preinstall', 'postinstall', 'preuninstall']): sys.exit(0) sys.exit(1) except: sys.exit(1) " 2>/dev/null; thenbash PERM_CHECK=$(python3 -c " import json try: d = json.load(open('$SKILL_DIR/skill.json')) req = d.get('requires', {})Technical Analysis
Shell variables are inserted directly into Python source passed to
python3 -c. Shell quoting does not make these values safe as Python string literals. A path containing a single quote, closing parenthesis, semicolon, or newline can terminate theopen('...')expression and append arbitrary Python statements.ALLOWLIST_FILEis directly supplied through the public--allowlistoption.SKILL_DIRis also supplied by the caller, whilepkg_fileinherits that directory path. Consequently, exploitation does not require modifying a scanned source file: a crafted path alone can become executable Python source.The broad
except: passand redirection of standard error make exploitation attempts and resulting parser failures less visible.Attack Path
- The attacker supplies an allowlist pathname containing Python syntax, or provides a skill inside a directory whose name contains such syntax.
- The path is accepted by the shell argument par ...[truncated 1149 chars]
- Remediation
View remediation
Remediation Suggestions
-
Never interpolate pathnames into code supplied to
python3 -c. -
Pass paths as positional arguments and read them through
sys.argv:bash ALLOWLISTED_SKILLS=$( python3 -c ' import json, sys with open(sys.argv[1], encoding="utf-8") as stream: data = json.load(stream) for skill in data.get("skills", []): print(skill.get("name", "")) ' "$ALLOWLIST_FILE" ) -
Apply the same correction to the
package.jsonandskill.jsonparsing blocks. -
Prefer a dedicated Python helper module over several dynamically constructed inline programs.
-
Replace broad exception suppression with explicit errors and a fail-closed exit status.
-
Add regression tests using paths containing spaces, quotes, newlines, semicolons, Unicode characters, and leading hyphens.
-
Run audits in a sandbox with no sensitive environment variables, restricted filesystem access, and disabled outbound networking.
-
