{"title":"Arbitrary Code Execution via `subprocess.Popen` `executable` Parameter Injection","language":"Python","severity":"Critical","cwe":"CWE-78","source_lines":[5],"flow_lines":[5,7],"sink_lines":[7],"vulnerable_code":"import subprocess\nimport os\n\ndef deploy_container_to_cloud(cloud_provider, container_image, region_code):\n    deployment_tool = os.getenv('CLOUD_CLI_PATH', '/usr/bin/cloud')\n    cmd_args = ['deploy', '--provider', cloud_provider, '--image', container_image, '--region', region_code]\n    proc = subprocess.Popen(cmd_args, executable=deployment_tool, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    output, errors = proc.communicate()\n    if proc.returncode == 0:\n        return {'status': 'deployed', 'output': output.decode()}\n    return {'status': 'failed', 'error': errors.decode()}","explanation":"The function uses an untrusted environment variable CLOUD_CLI_PATH to specify the executable parameter in subprocess.Popen. An attacker who can control this environment variable can execute arbitrary binaries with the provided command-line arguments, leading to complete system compromise.","remediation":"The fix validates the CLOUD_CLI_PATH environment variable against a strict allowlist of known, trusted CLI executable paths after resolving symlinks with os.path.realpath(). Additionally, all user-supplied arguments (cloud_provider, container_image, region_code) are validated against expected patterns to prevent argument injection attacks.","secure_code":"import subprocess\nimport os\n\nALLOWED_CLI_PATHS = (\n    '/usr/bin/cloud',\n    '/usr/local/bin/cloud',\n    '/usr/bin/aws',\n    '/usr/local/bin/aws',\n    '/usr/bin/az',\n    '/usr/local/bin/az',\n    '/usr/bin/gcloud',\n    '/usr/local/bin/gcloud',\n)\n\nALLOWED_PROVIDERS = ('aws', 'azure', 'gcp')\n\ndef deploy_container_to_cloud(cloud_provider, container_image, region_code):\n    # Validate cloud provider\n    if cloud_provider not in ALLOWED_PROVIDERS:\n        return {'status': 'failed', 'error': f'Invalid cloud provider: {cloud_provider}'}\n\n    # Validate container image name (alphanumeric, dashes, underscores, slashes, colons, dots)\n    import re\n    if not re.match(r'^[a-zA-Z0-9._/:@-]+$', container_image):\n        return {'status': 'failed', 'error': 'Invalid container image name'}\n\n    # Validate region code (alphanumeric and dashes only)\n    if not re.match(r'^[a-zA-Z0-9-]+$', region_code):\n        return {'status': 'failed', 'error': 'Invalid region code'}\n\n    # Resolve and validate the deployment tool path\n    deployment_tool = os.getenv('CLOUD_CLI_PATH', '/usr/bin/cloud')\n    resolved_path = os.path.realpath(deployment_tool)\n\n    if resolved_path not in ALLOWED_CLI_PATHS:\n        return {'status': 'failed', 'error': f'Untrusted CLI path: {resolved_path}'}\n\n    if not os.path.isfile(resolved_path):\n        return {'status': 'failed', 'error': f'CLI tool not found: {resolved_path}'}\n\n    cmd_args = [resolved_path, 'deploy', '--provider', cloud_provider, '--image', container_image, '--region', region_code]\n    proc = subprocess.Popen(cmd_args, executable=resolved_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    output, errors = proc.communicate()\n    if proc.returncode == 0:\n        return {'status': 'deployed', 'output': output.decode()}\n    return {'status': 'failed', 'error': errors.decode()}"}