{"title":"Unsafe Dynamic Import via importlib.import_module()","language":"Python","severity":"Critical","cwe":"CWE-94","source_lines":[8,9],"flow_lines":[8,9,10,11],"sink_lines":[11],"vulnerable_code":"import importlib\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\n\n@app.route('/api/cloud/execute', methods=['POST'])\ndef execute_cloud_provider_action():\n    provider = request.json.get('cloud_provider', 'aws')\n    action = request.json.get('action', 'list_instances')\n    module_path = f\"cloud_integrations.{provider}.{action}\"\n    try:\n        cloud_module = importlib.import_module(module_path)\n        result = cloud_module.execute(request.json.get('params', {}))\n        return jsonify({'status': 'success', 'data': result})\n    except Exception as e:\n        return jsonify({'status': 'error', 'message': str(e)}), 500","explanation":"The application dynamically constructs a module path using unsanitized user input from 'cloud_provider' and 'action' parameters, then passes it to importlib.import_module(). An attacker can manipulate these inputs to import arbitrary Python modules, leading to arbitrary code execution through module imports like os, subprocess, or any installed package.","remediation":"The fix introduces strict allowlists for both cloud providers and their associated actions, validating user input against these predefined sets before any dynamic import occurs. This prevents attackers from injecting arbitrary module paths since only explicitly permitted provider/action combinations can reach the importlib.import_module() call. Additionally, the error response no longer exposes internal exception details to prevent information leakage.","secure_code":"import importlib\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\n\n# Allowlist of valid cloud providers and actions\nALLOWED_PROVIDERS = {'aws', 'azure', 'gcp'}\nALLOWED_ACTIONS = {\n    'aws': {'list_instances', 'start_instance', 'stop_instance', 'describe_vpcs', 'list_buckets'},\n    'azure': {'list_vms', 'start_vm', 'stop_vm', 'list_resource_groups', 'list_storage_accounts'},\n    'gcp': {'list_instances', 'start_instance', 'stop_instance', 'list_buckets', 'list_networks'}\n}\n\n@app.route('/api/cloud/execute', methods=['POST'])\ndef execute_cloud_provider_action():\n    if not request.json:\n        return jsonify({'status': 'error', 'message': 'Request body must be JSON'}), 400\n\n    provider = request.json.get('cloud_provider', 'aws')\n    action = request.json.get('action', 'list_instances')\n\n    # Validate provider against allowlist\n    if provider not in ALLOWED_PROVIDERS:\n        return jsonify({'status': 'error', 'message': f'Invalid cloud provider. Allowed: {sorted(ALLOWED_PROVIDERS)}'}), 400\n\n    # Validate action against provider-specific allowlist\n    if action not in ALLOWED_ACTIONS.get(provider, set()):\n        return jsonify({'status': 'error', 'message': f'Invalid action for provider {provider}. Allowed: {sorted(ALLOWED_ACTIONS[provider])}'}), 400\n\n    module_path = f\"cloud_integrations.{provider}.{action}\"\n    try:\n        cloud_module = importlib.import_module(module_path)\n        result = cloud_module.execute(request.json.get('params', {}))\n        return jsonify({'status': 'success', 'data': result})\n    except Exception as e:\n        return jsonify({'status': 'error', 'message': 'An internal error occurred while executing the action'}), 500"}