{"title":"Command Injection via subprocess.run(shell=True)","language":"Python","severity":"Critical","cwe":"CWE-78","source_lines":[3,4],"flow_lines":[3,4,5,3,7],"sink_lines":[5,7],"vulnerable_code":"import subprocess\n\ndef provision_iot_device(device_mac, firmware_url):\n    sanitized_mac = device_mac.replace(':', '')\n    deployment_cmd = f\"curl -s {firmware_url} | tee /var/iot/firmware_{sanitized_mac}.bin && sha256sum /var/iot/firmware_{sanitized_mac}.bin\"\n    result = subprocess.run(deployment_cmd, shell=True, capture_output=True, text=True)\n    if result.returncode == 0:\n        verification_hash = result.stdout.split()[0]\n        subprocess.run(f\"logger 'IoT device {device_mac} provisioned with hash {verification_hash}'\", shell=True)\n        return {\"status\": \"provisioned\", \"checksum\": verification_hash}\n    return {\"status\": \"failed\", \"error\": result.stderr}","explanation":"The function accepts untrusted input (device_mac and firmware_url) and directly interpolates them into shell commands executed via subprocess.run(shell=True). Although device_mac has minimal sanitization (colon removal), both parameters can be exploited to inject arbitrary shell commands through metacharacters like semicolons, backticks, or pipe operators.","remediation":"The fix eliminates all shell=True usage by using subprocess with argument lists for curl, replacing sha256sum with Python's hashlib for checksum computation, and replacing the shell logger command with Python's logging module. Input validation is added to strictly verify the MAC address format and firmware URL before processing.","secure_code":"import subprocess\nimport re\nimport hashlib\nimport logging\nimport os\nfrom urllib.parse import urlparse\n\nlogger = logging.getLogger(__name__)\n\ndef provision_iot_device(device_mac, firmware_url):\n    # Validate MAC address format strictly\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 = urlparse(firmware_url)\n    if parsed_url.scheme not in ('http', 'https'):\n        return {\"status\": \"failed\", \"error\": \"Invalid firmware URL scheme\"}\n    if not parsed_url.hostname:\n        return {\"status\": \"failed\", \"error\": \"Invalid firmware URL\"}\n\n    sanitized_mac = device_mac.replace(':', '')\n    firmware_path = os.path.join('/var/iot', f'firmware_{sanitized_mac}.bin')\n\n    # Download firmware using subprocess without shell=True\n    curl_result = subprocess.run(\n        ['curl', '-s', '-o', firmware_path, '--', firmware_url],\n        capture_output=True, text=True\n    )\n    if curl_result.returncode != 0:\n        return {\"status\": \"failed\", \"error\": curl_result.stderr}\n\n    # Compute SHA256 checksum using Python's hashlib instead of shell command\n    sha256_hash = hashlib.sha256()\n    try:\n        with open(firmware_path, 'rb') as f:\n            for chunk in iter(lambda: f.read(8192), b''):\n                sha256_hash.update(chunk)\n    except IOError as e:\n        return {\"status\": \"failed\", \"error\": str(e)}\n\n    verification_hash = sha256_hash.hexdigest()\n\n    # Use Python's logging instead of shell logger command\n    logger.info(f'IoT device {device_mac} provisioned with hash {verification_hash}')\n\n    return {\"status\": \"provisioned\", \"checksum\": verification_hash}"}