{"title":"Command Injection via subprocess.run(shell=True)","language":"Python","severity":"Critical","cwe":"CWE-78","source_lines":[3],"flow_lines":[3,5,3,6,7],"sink_lines":[5,7],"vulnerable_code":"import subprocess\n\ndef provision_iot_device(device_mac, firmware_url):\n    validation_cmd = f\"ping -c 2 {device_mac.replace(':', '-')}.local && curl -o /tmp/fw.bin {firmware_url}\"\n    result = subprocess.run(validation_cmd, shell=True, capture_output=True, text=True)\n    if result.returncode == 0:\n        flash_cmd = f\"iot-flasher --device {device_mac} --firmware /tmp/fw.bin\"\n        subprocess.run(flash_cmd, shell=True)\n        return {\"status\": \"provisioned\", \"device\": device_mac}\n    return {\"status\": \"failed\", \"error\": result.stderr}","explanation":"The function accepts untrusted user input (device_mac and firmware_url) and directly interpolates them into shell commands executed via subprocess.run(shell=True). An attacker can inject arbitrary shell commands through these parameters, leading to complete system compromise.","remediation":"The fix eliminates command injection by replacing all subprocess.run(shell=True) calls with argument-list form (shell=False), which prevents shell interpretation of special characters. Additionally, strict input validation is applied to both the MAC address (regex whitelist) and firmware URL (scheme validation and dangerous character rejection) before they are used in any subprocess call.","secure_code":"import subprocess\nimport re\nfrom urllib.parse import urlparse\n\n\ndef validate_mac_address(mac):\n    \"\"\"Validate that the input is a properly formatted MAC address.\"\"\"\n    pattern = r'^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'\n    if not re.match(pattern, mac):\n        raise ValueError(f\"Invalid MAC address format: {mac}\")\n    return mac\n\n\ndef validate_firmware_url(url):\n    \"\"\"Validate that the firmware URL is a proper HTTP/HTTPS URL with safe characters.\"\"\"\n    parsed = urlparse(url)\n    if parsed.scheme not in ('http', 'https'):\n        raise ValueError(f\"Invalid URL scheme: {parsed.scheme}. Only http and https are allowed.\")\n    if not parsed.hostname:\n        raise ValueError(\"URL must have a valid hostname.\")\n    if any(c in url for c in [';', '|', '&', '`', '$', '(', ')', '{', '}', '\\n', '\\r']):\n        raise ValueError(\"URL contains invalid characters.\")\n    return url\n\n\ndef provision_iot_device(device_mac, firmware_url):\n    device_mac = validate_mac_address(device_mac)\n    firmware_url = validate_firmware_url(firmware_url)\n\n    device_hostname = device_mac.replace(':', '-') + '.local'\n\n    ping_result = subprocess.run(\n        ['ping', '-c', '2', device_hostname],\n        capture_output=True, text=True\n    )\n    if ping_result.returncode != 0:\n        return {\"status\": \"failed\", \"error\": \"Device not reachable: \" + ping_result.stderr}\n\n    curl_result = subprocess.run(\n        ['curl', '-o', '/tmp/fw.bin', '--', firmware_url],\n        capture_output=True, text=True\n    )\n    if curl_result.returncode != 0:\n        return {\"status\": \"failed\", \"error\": \"Firmware download failed: \" + curl_result.stderr}\n\n    flash_result = subprocess.run(\n        ['iot-flasher', '--device', device_mac, '--firmware', '/tmp/fw.bin'],\n        capture_output=True, text=True\n    )\n    if flash_result.returncode != 0:\n        return {\"status\": \"failed\", \"error\": \"Flashing failed: \" + flash_result.stderr}\n\n    return {\"status\": \"provisioned\", \"device\": device_mac}"}