{"title":"Command Injection via subprocess.shell=True","language":"Python","severity":"Critical","cwe":"CWE-78","source_lines":[3],"flow_lines":[3,4,5],"sink_lines":[5],"vulnerable_code":"import subprocess\n\ndef provision_iot_device(device_mac, firmware_url):\n    log_path = f\"/var/log/iot/provision_{device_mac}.log\"\n    cmd = f\"wget {firmware_url} -O /tmp/firmware.bin && sha256sum /tmp/firmware.bin > {log_path}\"\n    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)\n    if result.returncode == 0:\n        return {\"status\": \"provisioned\", \"device\": device_mac, \"log\": log_path}\n    return {\"status\": \"failed\", \"error\": result.stderr}","explanation":"The function takes user-controlled parameters (device_mac, firmware_url) and directly interpolates them into a shell command string without any sanitization or validation. This command is then executed via subprocess.run() with shell=True, allowing an attacker to inject arbitrary shell commands through either parameter.","remediation":"The fix eliminates command injection by avoiding shell=True and instead using subprocess with argument lists, which prevents shell metacharacter interpretation. Input validation is added to ensure the MAC address matches a strict regex pattern and the firmware URL uses only http/https schemes. File writing is done via Python's built-in file I/O instead of shell redirection.","secure_code":"import subprocess\nimport re\nimport urllib.parse\n\ndef provision_iot_device(device_mac, firmware_url):\n    # Validate MAC address format\n    if not re.match(r'^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$', device_mac):\n        return {\"status\": \"failed\", \"error\": \"Invalid MAC address format\"}\n    \n    # Validate firmware URL\n    parsed_url = urllib.parse.urlparse(firmware_url)\n    if parsed_url.scheme not in ('http', 'https'):\n        return {\"status\": \"failed\", \"error\": \"Invalid URL scheme. Only http and https are allowed.\"}\n    if not parsed_url.hostname:\n        return {\"status\": \"failed\", \"error\": \"Invalid URL: no hostname found\"}\n    \n    log_path = f\"/var/log/iot/provision_{device_mac}.log\"\n    firmware_path = \"/tmp/firmware.bin\"\n    \n    # Download firmware using subprocess without shell=True\n    wget_result = subprocess.run(\n        [\"wget\", firmware_url, \"-O\", firmware_path],\n        capture_output=True, text=True\n    )\n    if wget_result.returncode != 0:\n        return {\"status\": \"failed\", \"error\": wget_result.stderr}\n    \n    # Generate checksum using subprocess without shell=True\n    sha_result = subprocess.run(\n        [\"sha256sum\", firmware_path],\n        capture_output=True, text=True\n    )\n    if sha_result.returncode != 0:\n        return {\"status\": \"failed\", \"error\": sha_result.stderr}\n    \n    # Write checksum to log file using Python file I/O\n    with open(log_path, 'w') as log_file:\n        log_file.write(sha_result.stdout)\n    \n    return {\"status\": \"provisioned\", \"device\": device_mac, \"log\": log_path}"}