# Arbitrary Code Execution via `subprocess.Popen` `executable` Parameter Injection

Language: Python
Severity: Critical
CWE: CWE-78

## Source
5

## Flow
5-7

## Sink
7

## Vulnerable Code
```python
import subprocess
import os

def deploy_container_to_cloud(cloud_provider, container_image, region_code):
    deployment_tool = os.getenv('CLOUD_CLI_PATH', '/usr/bin/cloud')
    cmd_args = ['deploy', '--provider', cloud_provider, '--image', container_image, '--region', region_code]
    proc = subprocess.Popen(cmd_args, executable=deployment_tool, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output, errors = proc.communicate()
    if proc.returncode == 0:
        return {'status': 'deployed', 'output': output.decode()}
    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
```python
import subprocess
import os

ALLOWED_CLI_PATHS = (
    '/usr/bin/cloud',
    '/usr/local/bin/cloud',
    '/usr/bin/aws',
    '/usr/local/bin/aws',
    '/usr/bin/az',
    '/usr/local/bin/az',
    '/usr/bin/gcloud',
    '/usr/local/bin/gcloud',
)

ALLOWED_PROVIDERS = ('aws', 'azure', 'gcp')

def deploy_container_to_cloud(cloud_provider, container_image, region_code):
    # Validate cloud provider
    if cloud_provider not in ALLOWED_PROVIDERS:
        return {'status': 'failed', 'error': f'Invalid cloud provider: {cloud_provider}'}

    # Validate container image name (alphanumeric, dashes, underscores, slashes, colons, dots)
    import re
    if not re.match(r'^[a-zA-Z0-9._/:@-]+$', container_image):
        return {'status': 'failed', 'error': 'Invalid container image name'}

    # Validate region code (alphanumeric and dashes only)
    if not re.match(r'^[a-zA-Z0-9-]+$', region_code):
        return {'status': 'failed', 'error': 'Invalid region code'}

    # Resolve and validate the deployment tool path
    deployment_tool = os.getenv('CLOUD_CLI_PATH', '/usr/bin/cloud')
    resolved_path = os.path.realpath(deployment_tool)

    if resolved_path not in ALLOWED_CLI_PATHS:
        return {'status': 'failed', 'error': f'Untrusted CLI path: {resolved_path}'}

    if not os.path.isfile(resolved_path):
        return {'status': 'failed', 'error': f'CLI tool not found: {resolved_path}'}

    cmd_args = [resolved_path, 'deploy', '--provider', cloud_provider, '--image', container_image, '--region', region_code]
    proc = subprocess.Popen(cmd_args, executable=resolved_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output, errors = proc.communicate()
    if proc.returncode == 0:
        return {'status': 'deployed', 'output': output.decode()}
    return {'status': 'failed', 'error': errors.decode()}
```
