T09 · Insecure Skill Coding Practices
- Location
- nest-sdm.sh:77
- Finding
- Python Source Injection Through CLI Arguments<![CDATA[ ## Vulnerability Details **File Location**: `nest-sdm.sh`, lines 77-115 **Vulnerability Type**: Python source injection caused by unsafe interpolation **Risk Level**: High ### Vulnerable Code ```bash # --- Temperature conversion --- f_to_c() { python3 -c "print(round(($1 - 32) * 5/9, 1))"; } c_to_f() { python3 -c "print(round($1 * 9/5 + 32, 1))"; } # --- Device ID resolution --- # Auto-discover device IDs by type, optionally filtered by --name # Usage: get_device_id <TYPE> [name_filter] # name_filter matches against customName or room displayName (case-insensitive, substring) get_device_id() { local dtype="$1" local name_filter="${2:-}" api_get "devices" | python3 -c " import sys, json devices = json.load(sys.stdin).get('devices', []) name_filter = '''${name_filter}'''.strip().lower() matches = [] for d in devices: if d['type'] == 'sdm.devices.types.${dtype}': matches.append(d) if not matches: print(f'Error: No ${dtype} device found.', file=sys.stderr) sys.exit(1) if name_filter: filtered = [] for d in matches: custom = d.get('traits', {}).get('sdm.devices.traits.Info', {}).get('customName', '').lower() room = (d.get('parentRelations', [{}])[0].get('displayName', '') or '').lower() if name_filter in custom or name_filter in room: filtered.append(d) if not filtered: avail = [] for d in matches: custom = d.get('traits', {}).get('sdm.devices.traits.Info', {}).get('customName', '') room = d.get('parentRelations', [{}])[0].get('displayName', '') label = custom or room or '(unnamed)' avail.append(label) print(f'Error: No ${dtype} matching \"{name_filter}\". Available: {\", \".join(avail)}', file=sys.stderr) sys.exit(1) matches = filtered print(matches[0]['name'].split('/')[-1]) " } ``` A related instance occurs at line 414: ```bash echo "✅ Fan ON for ${duration}s ($(python3 -c "print(${duration} ...[truncated 2127 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Never construct Python source from shell variables. - Pass all values as positional arguments: ```bash f_to_c() { python3 - "$1" <<'PY' import sys value = float(sys.argv[1]) print(round((value - 32) * 5 / 9, 1)) PY } ``` - Pass `name_filter` and `dtype` through `sys.argv`, while continuing to read API JSON from standard input. - Validate temperatures against documented Nest operating ranges before making API calls. - Validate fan duration as a decimal integer and enforce a reasonable minimum and maximum. - Validate modes and device types with explicit allowlists. - Avoid `eval`, dynamically generated Python, and nested command interpolation. - Add regression tests containing quotes, triple quotes, backslashes, newlines, semicolons, and Python syntax in every CLI parameter. ]]>
