T09 · Insecure Skill Coding Practices
- Location
scripts/script.sh:296- Finding
Remote API Data Is Interpolated into Executable Python Source
- Content
View full analysis
/dev/null)" # Also get today's rate for comparison local today_url="${API_BASE}/latest?from=${from}&to=${to}" local today_response today_rate today_date today_response="$(api_get "$today_url")" || true today_rate="$(echo "$today_response" | python3 -c " import sys, json d = json.load(sys.stdin) rates = d.get('rates', {}) to = '${to}' if to in rates: print(rates[to]) else: print('N/A') " 2>/dev/null || echo "N/A")" today_date="$(json_get "$today_response" "date" || echo "N/A")" local change_str="N/A" if [[ "$rate_val" != "N/A" && "$today_rate" != "N/A" ]]; then change_str="$(python3 -c " h = float('${rate_val}') t = float('${today_rate}') diff = t - h pct = (diff / h) * 100 sign = '+' if diff >= 0 else '' print(f'{sign}{diff:.6f} ({sign}{pct:.2f}%)') " 2>/dev/null || echo "N/A")" fi ``` ### Technical Analysis The `rate_val` and `today_rate` variables originate from JSON returned by the external `api.frankfurter.app` service. Although the responses are parsed as JSON, the values selected from the `rates` object are printed without verifying that they are JSON numbers. These remotely supplied values are subsequently interpolated directly into Python source passed to `python3 -c`: ```python h = float('${rate_val}') t = float('${today_rate}') ``` Shell quoting does not make this safe because variable expansion occurs before Python parses the generated source. If either value contains Python syntax that terminates the quoted string, that syntax becomes part of the program executed by Python. For example, a specially ...[truncated 1898 chars]- Remediation
View remediation
= 0 else '' print(f'{sign}{diff:.6f} ({sign}{pct:.2f}%)') PY )" || change_str="N/A" ``` Additional hardening should include: 1. Validate the decoded API schema before using it: - Require the top-level response to be an object. - Require `rates` to be an object. - Require the requested currency entry to be an integer or floating-point JSON number. - Reject strings, booleans, null values, arrays, and objects. 2. Reject non-finite values, zero, and negative exchange rates. 3. Apply the same data-versus-code separation to all `python3 -c` calls. Values such as JSON keys and formatting inputs should be supplied through `sys.argv` or standard input rather than interpolated into source. 4. Preserve HTTPS certificate verification and avoid adding options such as `curl -k`. 5. Add regression tests containing quote characters and Python syntax in mocked API rate values, confirming that the input is rejected and no code is executed. ]]>
