# Import Hijacking via sys.path Precedence

Language: Python
Severity: Critical
CWE: CWE-426

## Source
3

## Flow
3-5-7

## Sink
7

## Vulnerable Code
```python
import sys
import os
def load_cloud_credentials(tenant_id):
    user_plugin_dir = f"/var/app/tenants/{tenant_id}/plugins"
    if os.path.exists(user_plugin_dir):
        sys.path.insert(0, user_plugin_dir)
    import azure_auth
    creds = azure_auth.get_service_principal()
    return creds
def authenticate_azure_resource(tenant_id, resource_name):
    credentials = load_cloud_credentials(tenant_id)
    return credentials.connect(resource_name)
```

## Explanation

The tenant_id parameter flows from untrusted input into a path construction that is added to sys.path, allowing an attacker to inject a malicious module path. When the import statement executes, Python searches sys.path in order and loads the attacker's malicious azure_auth module instead of the legitimate one, enabling credential theft or code execution.

## Remediation

The fix eliminates the sys.path manipulation entirely and instead loads the azure_auth module exclusively from a trusted, hardcoded file path using importlib.util.spec_from_file_location. Additionally, the tenant_id is strictly validated against an allowlist regex pattern to prevent path traversal attacks, ensuring no attacker-controlled input can influence module loading.

## Secure Code
```python
import sys
import os
import importlib
import importlib.util
import re

ALLOWED_TENANT_ID_PATTERN = re.compile(r'^[a-zA-Z0-9_-]+$')
TRUSTED_AZURE_AUTH_PATH = "/opt/app/lib/azure_auth.py"

def _load_trusted_azure_auth():
    """Load azure_auth exclusively from the trusted, known location."""
    spec = importlib.util.spec_from_file_location("azure_auth", TRUSTED_AZURE_AUTH_PATH)
    if spec is None or spec.loader is None:
        raise ImportError(f"Cannot load trusted azure_auth module from {TRUSTED_AZURE_AUTH_PATH}")
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

def _validate_tenant_id(tenant_id):
    """Validate tenant_id to prevent path traversal attacks."""
    if not tenant_id or not isinstance(tenant_id, str):
        raise ValueError("Invalid tenant_id: must be a non-empty string")
    if not ALLOWED_TENANT_ID_PATTERN.match(tenant_id):
        raise ValueError(f"Invalid tenant_id: contains disallowed characters: {tenant_id}")
    return tenant_id

def load_cloud_credentials(tenant_id):
    """Load cloud credentials using only the trusted azure_auth module.
    
    Tenant-specific plugin directories are NOT added to sys.path to prevent
    import hijacking attacks.
    """
    validated_tenant_id = _validate_tenant_id(tenant_id)
    
    # Load azure_auth from a trusted, fixed path only
    azure_auth = _load_trusted_azure_auth()
    creds = azure_auth.get_service_principal(validated_tenant_id)
    return creds

def authenticate_azure_resource(tenant_id, resource_name):
    credentials = load_cloud_credentials(tenant_id)
    return credentials.connect(resource_name)
```
