T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:78
- Finding
- Arbitrary Python Code Execution Through Unsafe Variable Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 78–85, 100–114, and 150–164 **Vulnerability Type**: Python code injection **Risk Level**: High ### Vulnerable Code At lines 78–85, the user-controlled package name is embedded directly into Python source: ```bash CURRENT=$(python3 -c " import json d = json.load(open('package.json')) v = d.get('dependencies',{}).get('$PACKAGE') or d.get('devDependencies',{}).get('$PACKAGE') or 'not found' print(v) " 2>/dev/null) echo "Current: $PACKAGE@$CURRENT" ``` At lines 100–114, the repository-derived `CURRENT` value is embedded into another Python program: ```bash npm info "$PACKAGE" versions --json 2>/dev/null | python3 -c " import json, sys, re try: versions = json.load(sys.stdin) current = '$CURRENT'.lstrip('^~>=') current_major = current.split('.')[0] if current != 'not found' else '0' latest = versions[-1] if isinstance(versions, list) else versions latest_major = latest.split('.')[0] if current_major != latest_major: print(f'⚠️ MAJOR version change: {current} → {latest} (likely breaking changes)') else: print(f'✅ Same major version: {current} → {latest} (should be backward compatible)') except Exception as e: print(f'Could not check versions: {e}') " 2>/dev/null ``` At lines 150–164, the package name is again embedded directly into generated Python source: ```bash python3 -c " import json lock = json.load(open('package-lock.json')) pkg = '$PACKAGE' rdeps = [] packages = lock.get('packages', lock.get('dependencies', {})) for name, info in packages.items(): deps = info.get('dependencies', {}) if pkg in deps: clean_name = name.replace('node_modules/', '') rdeps.append(f'{clean_name} (requires {pkg}@{deps[pkg]})') if rdeps: print(f'{len(rdeps)} packages also depend on {pkg}:') for r in rdeps[:20]: print(f' {r}') else: print(f'No other packages depend on {pkg} (leaf dependency)') " 2>/dev/null ``` ## ...[truncated 3137 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Never interpolate command arguments or repository-derived values into source code passed to `python3 -c`. Pass all dynamic values as positional arguments or environment variables and treat them strictly as data. For example, replace the current-version lookup with: ```bash CURRENT=$(python3 - "$PACKAGE" <<'PY' import json import sys package = sys.argv[1] with open("package.json", encoding="utf-8") as handle: data = json.load(handle) version = ( data.get("dependencies", {}).get(package) or data.get("devDependencies", {}).get(package) or "not found" ) print(version) PY ) ``` Pass both the current version and registry response safely when checking major versions: ```bash npm info "$PACKAGE" versions --json 2>/dev/null | python3 - "$CURRENT" <<'PY' import json import sys current = sys.argv[1].lstrip("^~>=") versions = json.load(sys.stdin) latest = versions[-1] if isinstance(versions, list) else versions current_major = current.split(".")[0] if current != "not found" else "0" latest_major = latest.split(".")[0] if current_major != latest_major: print(f"Major version change: {current} -> {latest}") else: print(f"Same major version: {current} -> {latest}") PY ``` Likewise, pass the package name as `sys.argv[1]` in the reverse-dependency analysis: ```bash python3 - "$PACKAGE" <<'PY' import json import sys package = sys.argv[1] with open("package-lock.json", encoding="utf-8") as handle: lock = json.load(handle) # Continue analysis using `package` only as data. PY ``` Additional hardening measures: 1. Validate npm package names against an appropriate allowlist pattern before registry operations. 2. Do not rely on shell or Python quote escaping as the primary defense; use argument boundaries. 3. Treat all manifest and lockfile values as untrusted repository input. 4. Avoid suppressing all errors during security-relevant operations, because doing so can conceal malformed input and attempted explo ...[truncated 254 chars]
