{"title":"Path Traversal via pathlib.resolve on Untrusted Filename","language":"Python","severity":"High","cwe":"CWE-22","source_lines":[8],"flow_lines":[8,10,11],"sink_lines":[11],"vulnerable_code":"from pathlib import Path\nfrom flask import Flask, request, send_file\n\napp = Flask(__name__)\n\n@app.route('/api/v1/download_model')\ndef fetch_trained_model():\n    model_name = request.args.get('model_id', 'default.h5')\n    models_dir = Path('/opt/ml/models')\n    target_model = (models_dir / model_name).resolve()\n    if target_model.exists():\n        return send_file(target_model, as_attachment=True)\n    return {'error': 'Model not found'}, 404","explanation":"The application accepts user-controlled input via 'model_id' parameter without validation and constructs a file path using pathlib. While resolve() canonicalizes the path, there's no verification that the resolved path remains within the intended '/opt/ml/models' directory, allowing path traversal attacks to access arbitrary files on the system.","remediation":"The fix adds a path containment check using `is_relative_to()` after resolving the path, which verifies that the resolved target path remains within the intended '/opt/ml/models' directory. If the resolved path escapes the allowed directory (e.g., via '../' sequences), the request is rejected with a 400 error before any file access occurs. The models_dir is also resolved first to ensure consistent comparison.","secure_code":"from pathlib import Path\nfrom flask import Flask, request, send_file\n\napp = Flask(__name__)\n\n@app.route('/api/v1/download_model')\ndef fetch_trained_model():\n    model_name = request.args.get('model_id', 'default.h5')\n    models_dir = Path('/opt/ml/models').resolve()\n    target_model = (models_dir / model_name).resolve()\n    if not target_model.is_relative_to(models_dir):\n        return {'error': 'Invalid model identifier'}, 400\n    if target_model.exists():\n        return send_file(target_model, as_attachment=True)\n    return {'error': 'Model not found'}, 404"}