# Import Hijacking via sys.path Precedence Manipulation

Language: Python
Severity: Critical
CWE: CWE-426

## Source
11

## Flow
11-12-13-4-5

## Sink
5

## Vulnerable Code
```python
import sys
import os
def configure_ml_model_loader(custom_plugin_dir):
    plugin_path = os.path.join('/var/ml/plugins', custom_plugin_dir)
    sys.path.insert(0, plugin_path)
    import tensorflow_adapter
    import numpy
    model_config = tensorflow_adapter.load_config()
    data_processor = numpy.array(model_config['weights'])
    return tensorflow_adapter.initialize_model(data_processor)
def deploy_inference_engine(user_workspace):
    workspace_dir = f"/tmp/workspaces/{user_workspace}"
    configure_ml_model_loader(workspace_dir)
    return "Model deployed successfully"
```

## Explanation

The user_workspace parameter is directly incorporated into a file path and then inserted at the beginning of sys.path via sys.path.insert(0, plugin_path). This allows an attacker to control which directory Python searches first for module imports, enabling them to place malicious tensorflow_adapter or numpy modules that will be imported instead of legitimate libraries.

## Remediation

The fix prevents import hijacking by: (1) validating user input against a strict allowlist pattern that rejects path traversal characters, (2) resolving and verifying the canonical path stays within the allowed base directory, (3) using explicit file-based module loading via importlib for the custom adapter instead of manipulating sys.path precedence, and removing any temporary path additions after use.

## Secure Code
```python
import sys
import os
import re

ALLOWED_PLUGIN_BASE = '/var/ml/plugins'
ALLOWED_WORKSPACE_PATTERN = re.compile(r'^[a-zA-Z0-9_\-]+$')

def configure_ml_model_loader(custom_plugin_dir):
    if not ALLOWED_WORKSPACE_PATTERN.match(custom_plugin_dir):
        raise ValueError(f"Invalid plugin directory name: {custom_plugin_dir}. Only alphanumeric characters, hyphens, and underscores are allowed.")
    
    plugin_path = os.path.join(ALLOWED_PLUGIN_BASE, custom_plugin_dir)
    
    resolved_path = os.path.realpath(plugin_path)
    resolved_base = os.path.realpath(ALLOWED_PLUGIN_BASE)
    
    if not resolved_path.startswith(resolved_base + os.sep):
        raise ValueError(f"Plugin path escapes allowed directory: {custom_plugin_dir}")
    
    if not os.path.isdir(resolved_path):
        raise FileNotFoundError(f"Plugin directory does not exist: {resolved_path}")
    
    sys.path.append(resolved_path)
    
    import importlib
    import importlib.util
    
    adapter_file = os.path.join(resolved_path, 'tensorflow_adapter.py')
    if not os.path.isfile(adapter_file):
        raise FileNotFoundError(f"tensorflow_adapter.py not found in {resolved_path}")
    
    spec = importlib.util.spec_from_file_location("tensorflow_adapter", adapter_file)
    tensorflow_adapter = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(tensorflow_adapter)
    
    import numpy
    
    model_config = tensorflow_adapter.load_config()
    data_processor = numpy.array(model_config['weights'])
    
    if resolved_path in sys.path:
        sys.path.remove(resolved_path)
    
    return tensorflow_adapter.initialize_model(data_processor)

def deploy_inference_engine(user_workspace):
    if not user_workspace or not isinstance(user_workspace, str):
        raise ValueError("Invalid workspace identifier")
    
    if not ALLOWED_WORKSPACE_PATTERN.match(user_workspace):
        raise ValueError(f"Invalid workspace name: {user_workspace}. Only alphanumeric characters, hyphens, and underscores are allowed.")
    
    configure_ml_model_loader(user_workspace)
    return "Model deployed successfully"
```
