{"title":"Path Traversal via pathlib.Path.resolve on Untrusted Filename","language":"Python","severity":"High","cwe":"CWE-22","source_lines":[9],"flow_lines":[9,10,11,12],"sink_lines":[12],"vulnerable_code":"from pathlib import Path\nfrom flask import Flask, request, send_file\n\napp = Flask(__name__)\nMODEL_STORAGE = \"/opt/ml/trained_models\"\n\n@app.route('/api/v2/download-model', methods=['GET'])\ndef fetch_trained_model():\n    model_identifier = request.args.get('model_name', 'default.h5')\n    model_path = Path(MODEL_STORAGE) / model_identifier\n    resolved = model_path.resolve()\n    if resolved.exists():\n        return send_file(str(resolved), as_attachment=True)\n    return {\"error\": \"Model not found\"}, 404","explanation":"The application accepts user-controlled input via 'model_name' parameter without validation and uses it to construct a file path. Although Path.resolve() is called, an attacker can still use path traversal sequences (../) to escape the MODEL_STORAGE directory and access arbitrary files on the filesystem, which are then served via send_file().","remediation":"The fix adds a validation check after resolving the path to ensure the resolved path starts with the resolved MODEL_STORAGE directory prefix (with a trailing slash to prevent partial directory name matches). If the resolved path escapes outside the allowed directory, the request is rejected with a 400 error before any file access occurs.","secure_code":"from pathlib import Path\nfrom flask import Flask, request, send_file\n\napp = Flask(__name__)\nMODEL_STORAGE = \"/opt/ml/trained_models\"\n\n@app.route('/api/v2/download-model', methods=['GET'])\ndef fetch_trained_model():\n    model_identifier = request.args.get('model_name', 'default.h5')\n    model_path = Path(MODEL_STORAGE) / model_identifier\n    resolved = model_path.resolve()\n    storage_resolved = Path(MODEL_STORAGE).resolve()\n    if not str(resolved).startswith(str(storage_resolved) + '/'):\n        return {\"error\": \"Invalid model name\"}, 400\n    if resolved.exists():\n        return send_file(str(resolved), as_attachment=True)\n    return {\"error\": \"Model not found\"}, 404"}