T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/discord-roster.sh:53
- Finding
- Arbitrary Python Code Execution Through the Members Filter Argument## Vulnerability Details **File Location**: `scripts/discord-roster.sh`, lines 53–66 **Vulnerability Type**: Unsanitized argument interpolation into dynamically constructed Python source **Risk Level**: High **Vulnerable Code**: ```bash cmd_members() { local guild_id="${1:?Usage: members <guild_id> [--bots|--humans]}" local filter="${2:-all}" local raw raw=$(api "/guilds/${guild_id}/members?limit=1000") python3 -c " import json, sys data = json.loads('''$( echo "$raw" | python3 -c "import sys; print(sys.stdin.read().replace(\"'\",\"\\\\'\"))" )''') if isinstance(data, dict) and 'message' in data: print(f\"API Error: {data['message']}\", file=sys.stderr) sys.exit(1) filt = '$filter' ``` ### Technical Analysis The second positional argument to the `members` command is stored in `filter` and inserted directly into the source string supplied to `python3 -c`. It is not validated or passed as inert data through `sys.argv`, standard input, or an environment variable. An attacker who can control this argument can include a single quote to terminate the Python string assigned to `filt`, append Python statements, and comment out the remaining source on that line. The injected Python can invoke operating-system commands through modules such as `os` or `subprocess`. Shell metacharacters introduced through ordinary parameter expansion are not re-evaluated as shell syntax, but this does not prevent exploitation: the resulting text is subsequently parsed as Python source by `python3 -c`. Exploitation requires the preceding Discord API request to complete successfully because the vulnerable Python invocation occurs after `raw=$(api ...)`. ### Attack Path 1. The attacker gains control over, or influences, the arguments used to invoke the Skill. 2. The attacker invokes `members` with a valid or accessible guild ID so the Discord API request succeeds. 3. The second argument is crafted to cl ...[truncated 1212 chars]
- Remediation
- ## Remediation Suggestions Do not embed command-line arguments in Python source. Pass the filter as a positional argument and validate it against an explicit allowlist before use. A hardened approach is: ```bash local filter="${2:-all}" case "$filter" in all|--bots|--humans) ;; *) die "Invalid member filter: $filter" ;; esac printf '%s' "$raw" | python3 -c ' import json import sys filt = sys.argv[1] data = json.load(sys.stdin) # Process data without constructing source from input. ' "$filter" ``` Additionally: - Pass API responses through standard input rather than embedding them into Python source. - Reject unexpected extra arguments. - Add regression tests containing quotes, newlines, backslashes, semicolons, and Python syntax. - Treat every command-line argument as untrusted, even when expected to come from an agent-generated command.
