T09 · Insecure Skill Coding Practices
- Location
scripts/set-balance.sh:13- Finding
Arbitrary Python Code Execution Through an Unescaped Balance Argument
- Content
View full analysis
Vulnerability Details
File Location:
scripts/set-balance.sh, lines 13–20
Vulnerability Type: Command injection through dynamically constructed Python source
Risk Level: HighVulnerable Code
bash python3 -c " import json with open('$BALANCE_FILE') as f: d = json.load(f) d['deepseek']['balance_rmb'] = float('$2'.replace(',','')) d['_last_processed'] = '' with open('$BALANCE_FILE', 'w') as f: json.dump(d, f, indent=2, ensure_ascii=False) print(f'DeepSeek balance set to ¥{float(\"$2\"):.2f}') "Technical Analysis
The second command-line argument,
$2, is interpolated directly into source code passed topython3 -c. Shell quoting does not make this safe because the expanded value becomes part of the generated Python program.An attacker who can control the DeepSeek balance argument can supply text that terminates the expected Python string literal and inserts additional Python statements. The injected statements execute when Python parses and runs the dynamically generated program. Numeric conversion does not provide effective validation because injected code can execute before a subsequent conversion error terminates the script.
This is an exploitable code-injection flaw rather than intended Skill behavior. Setting a numeric balance does not require constructing executable source code from user input.
Attack Path
- A user, automation rule, or agent invokes
set-balance deepseek <value>. - An attacker influences the supplied balance value, such as through copied instructions or another untrusted input source.
- The crafted value closes the Python string literal surrounding
$2and introduces additional Python statements. - The shell expands
$2inside the multilinepython3 -cargument. - Python executes the injected statements with the privileges and environment of the user running the Skill.
- The injected code can access local files, credentials, process ...[truncated 1010 chars]
- A user, automation rule, or agent invokes
- Remediation
View remediation
Remediation Suggestions
Do not interpolate command-line values into dynamically generated Python source. Pass the balance and file path as ordinary positional arguments:
bash if [[ $# -ne 2 ]]; then echo "Usage: set-balance deepseek <rmb>" >&2 exit 1 fi python3 - "$BALANCE_FILE" "$2" <<'PY' import json import math import sys path = sys.argv[1] try: value = float(sys.argv[2].replace(",", "")) except ValueError: raise SystemExit("Balance must be numeric") if not math.isfinite(value) or value < 0: raise SystemExit("Balance must be a finite, non-negative number") with open(path, encoding="utf-8") as f: data = json.load(f) data.setdefault("deepseek", {})["balance_rmb"] = value data["_last_processed"] = "" with open(path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) print(f"DeepSeek balance set to ¥{value:.2f}") PYAdditional hardening should include:
- Validate the exact number of arguments before referencing
$2. - Reject negative, non-finite, malformed, or implausibly large balances.
- Apply the same argument-passing approach to every value currently embedded in inline Python.
- Prefer a standalone Python script over
python3 -cfor maintainability and safer input handling. - Add regression tests using quotes, newlines, semicolons, Python expressions, and shell metacharacters to verify that inputs are treated solely as data.
- Write balance updates atomically and restrict the balance file's permissions to the intended user.
- Validate the exact number of arguments before referencing
