{"title":"Import Hijacking via sys.path Precedence","language":"Python","severity":"Critical","cwe":"CWE-427","source_lines":[3],"flow_lines":[3,4,5],"sink_lines":[5],"vulnerable_code":"import sys\nimport os\ndef load_cloud_credentials(bucket_name):\n    config_dir = os.path.join('/tmp/user_uploads', bucket_name)\n    sys.path.insert(0, config_dir)\n    import boto3_config\n    session = boto3_config.create_session()\n    return session.get_credentials()\ndef sync_s3_data(bucket):\n    creds = load_cloud_credentials(bucket)\n    print(f\"Syncing {bucket} with credentials: {creds['access_key']}\")\n    return creds","explanation":"The function accepts a user-controlled bucket_name parameter and directly inserts it into sys.path without validation. This allows an attacker to inject a malicious boto3_config.py module that will be imported, leading to arbitrary code execution when the import statement is reached.","remediation":"The fix eliminates the import hijacking vulnerability by: (1) removing all manipulation of sys.path, (2) loading configuration modules only from a trusted system directory (/etc/app/bucket_configs) using importlib.util with explicit file paths and path traversal checks, and (3) strictly validating the bucket_name input against AWS S3 naming conventions to prevent directory traversal or injection attacks.","secure_code":"import os\nimport re\nimport importlib.util\nimport hashlib\n\nALLOWED_BUCKET_PATTERN = re.compile(r'^[a-z0-9][a-z0-9.\\-]{1,61}[a-z0-9]$')\nTRUSTED_CONFIG_DIR = '/etc/app/bucket_configs'\n\ndef _validate_bucket_name(bucket_name):\n    \"\"\"Validate bucket name follows AWS S3 naming conventions.\"\"\"\n    if not bucket_name or not ALLOWED_BUCKET_PATTERN.match(bucket_name):\n        raise ValueError(f\"Invalid bucket name: {bucket_name}\")\n    if '..' in bucket_name or '/' in bucket_name or '\\\\' in bucket_name:\n        raise ValueError(f\"Invalid characters in bucket name: {bucket_name}\")\n    return bucket_name\n\ndef _load_config_module(config_path):\n    \"\"\"Safely load a configuration module from an explicit file path without modifying sys.path.\"\"\"\n    if not os.path.isfile(config_path):\n        raise FileNotFoundError(f\"Configuration file not found: {config_path}\")\n    \n    # Resolve the path and ensure it stays within the trusted directory\n    resolved_path = os.path.realpath(config_path)\n    trusted_resolved = os.path.realpath(TRUSTED_CONFIG_DIR)\n    if not resolved_path.startswith(trusted_resolved + os.sep):\n        raise PermissionError(f\"Configuration file is outside trusted directory: {resolved_path}\")\n    \n    spec = importlib.util.spec_from_file_location(\"boto3_config\", resolved_path)\n    if spec is None or spec.loader is None:\n        raise ImportError(f\"Cannot load module from: {resolved_path}\")\n    \n    module = importlib.util.module_from_spec(spec)\n    spec.loader.exec_module(module)\n    return module\n\ndef load_cloud_credentials(bucket_name):\n    \"\"\"Load AWS credentials from a trusted, validated configuration module.\"\"\"\n    validated_bucket = _validate_bucket_name(bucket_name)\n    config_path = os.path.join(TRUSTED_CONFIG_DIR, validated_bucket, 'boto3_config.py')\n    \n    boto3_config = _load_config_module(config_path)\n    \n    if not hasattr(boto3_config, 'create_session') or not callable(boto3_config.create_session):\n        raise AttributeError(\"Configuration module missing required 'create_session' function\")\n    \n    session = boto3_config.create_session()\n    return session.get_credentials()\n\ndef sync_s3_data(bucket):\n    \"\"\"Sync S3 data using validated bucket credentials.\"\"\"\n    creds = load_cloud_credentials(bucket)\n    print(f\"Syncing {bucket} with credentials: {creds['access_key']}\")\n    return creds"}