# Import Hijacking via sys.path Manipulation

Language: Python
Severity: Critical
CWE: CWE-427

## Source
13

## Flow
13-5-6-7

## Sink
7

## Vulnerable Code
```python
import sys
import os

def load_cloud_storage_plugin(plugin_dir, bucket_name):
    plugin_path = os.path.join('/var/app/plugins', plugin_dir)
    if os.path.exists(plugin_path):
        sys.path.insert(0, plugin_path)
    import s3_handler
    storage = s3_handler.S3Manager(bucket_name)
    return storage.get_connection()

def sync_aws_bucket(user_plugin, bucket):
    conn = load_cloud_storage_plugin(user_plugin, bucket)
    return conn.list_objects()
```

## Explanation

The application accepts user-controlled input (user_plugin parameter) and uses it to construct a path that is inserted at the beginning of sys.path without validation. This allows an attacker to inject a malicious directory containing a fake s3_handler module that will be imported instead of the legitimate one, leading to arbitrary code execution.

## Remediation

The fix eliminates sys.path manipulation entirely and instead uses importlib to load the module from a specific, validated file path. It validates that the plugin directory name contains only safe characters (alphanumeric, underscore, hyphen) to prevent path traversal, and additionally verifies the resolved absolute path remains within the allowed plugins base directory.

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

ALLOWED_PLUGINS_BASE = '/var/app/plugins'
ALLOWED_PLUGIN_NAMES = re.compile(r'^[a-zA-Z0-9_\-]+$')

def load_cloud_storage_plugin(plugin_dir, bucket_name):
    # Validate plugin_dir is a simple directory name (no path traversal)
    if not ALLOWED_PLUGIN_NAMES.match(plugin_dir):
        raise ValueError(f"Invalid plugin directory name: {plugin_dir}. Only alphanumeric, underscore, and hyphen characters are allowed.")
    
    plugin_path = os.path.join(ALLOWED_PLUGINS_BASE, plugin_dir)
    
    # Resolve to absolute path and verify it's still under the allowed base
    resolved_path = os.path.realpath(plugin_path)
    allowed_base_resolved = os.path.realpath(ALLOWED_PLUGINS_BASE)
    if not resolved_path.startswith(allowed_base_resolved + os.sep):
        raise ValueError(f"Plugin path escapes allowed directory: {plugin_dir}")
    
    if not os.path.isdir(resolved_path):
        raise FileNotFoundError(f"Plugin directory does not exist: {resolved_path}")
    
    # Load s3_handler module directly from the validated path instead of manipulating sys.path
    module_file = os.path.join(resolved_path, 's3_handler.py')
    if not os.path.isfile(module_file):
        raise FileNotFoundError(f"s3_handler.py not found in plugin directory: {resolved_path}")
    
    # Use importlib to load the module from a specific file path
    spec = importlib.util.spec_from_file_location('s3_handler', module_file)
    if spec is None or spec.loader is None:
        raise ImportError(f"Cannot load s3_handler module from: {module_file}")
    
    s3_handler = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(s3_handler)
    
    storage = s3_handler.S3Manager(bucket_name)
    return storage.get_connection()

def sync_aws_bucket(user_plugin, bucket):
    conn = load_cloud_storage_plugin(user_plugin, bucket)
    return conn.list_objects()
```
