{"title":"Path Traversal via unsanitized os.path.join() filename input","language":"Python","severity":"High","cwe":"CWE-22","source_lines":[9],"flow_lines":[9,10,11],"sink_lines":[11],"vulnerable_code":"import os\nfrom flask import Flask, request, send_file\n\napp = Flask(__name__)\nMODEL_STORAGE = '/var/ml/trained_models'\n\n@app.route('/api/v1/model/download', methods=['GET'])\ndef fetch_trained_model():\n    model_identifier = request.args.get('model_id', 'default.h5')\n    model_path = os.path.join(MODEL_STORAGE, model_identifier)\n    if os.path.exists(model_path):\n        return send_file(model_path, as_attachment=True)\n    return {'error': 'Model not found'}, 404","explanation":"The vulnerability exists because user-controlled input from 'model_id' parameter is directly concatenated into a file path using os.path.join() without validation, then passed to send_file(). An attacker can inject path traversal sequences (../) to access arbitrary files outside the intended MODEL_STORAGE directory.","remediation":"The fix applies two layers of defense: first, os.path.basename() strips any directory traversal components (like '../') from the user input, keeping only the filename portion. Second, os.path.realpath() resolves the final path and verifies it still resides within the intended MODEL_STORAGE directory, preventing any symlink or edge-case bypass.","secure_code":"import os\nfrom flask import Flask, request, send_file\n\napp = Flask(__name__)\nMODEL_STORAGE = '/var/ml/trained_models'\n\n@app.route('/api/v1/model/download', methods=['GET'])\ndef fetch_trained_model():\n    model_identifier = request.args.get('model_id', 'default.h5')\n    # Sanitize: extract only the basename to prevent directory traversal\n    safe_filename = os.path.basename(model_identifier)\n    if not safe_filename:\n        return {'error': 'Invalid model identifier'}, 400\n    model_path = os.path.join(MODEL_STORAGE, safe_filename)\n    # Verify the resolved path is still within the allowed directory\n    real_model_path = os.path.realpath(model_path)\n    real_storage_dir = os.path.realpath(MODEL_STORAGE)\n    if not real_model_path.startswith(real_storage_dir + os.sep):\n        return {'error': 'Invalid model identifier'}, 400\n    if os.path.exists(real_model_path):\n        return send_file(real_model_path, as_attachment=True)\n    return {'error': 'Model not found'}, 404"}