{"title":"Path Traversal via Unsanitized File Read in pathlib.Path.resolve()","language":"Python","severity":"High","cwe":"CWE-22","source_lines":[8,9],"flow_lines":[8,11,12,13,9,11,12,13],"sink_lines":[13],"vulnerable_code":"from pathlib import Path\nfrom flask import Flask, request, send_file\n\napp = Flask(__name__)\n\n@app.route('/iot/device/logs')\ndef fetch_device_logs():\n    device_id = request.args.get('device_id', 'default')\n    log_filename = request.args.get('log_file', 'status.log')\n    base_dir = Path('/var/iot/devices')\n    log_path = base_dir / device_id / log_filename\n    resolved = log_path.resolve()\n    if resolved.exists():\n        return send_file(str(resolved), mimetype='text/plain')\n    return {'error': 'Log file not found'}, 404","explanation":"The application accepts user-controlled device_id and log_file parameters without validation, constructs a file path using pathlib operations, and resolves it. Although resolve() is used, there is no check to ensure the resolved path remains within the intended base_dir boundary before passing it to send_file(), allowing path traversal attacks to access arbitrary files on the system.","remediation":"The fix adds two layers of defense: first, it rejects user inputs containing path traversal characters ('..', '/', '\\\\') in both device_id and log_filename parameters. Second, after resolving the full path, it verifies that the resolved path starts with the resolved base directory path, ensuring no traversal can escape the intended directory boundary.","secure_code":"from pathlib import Path\nfrom flask import Flask, request, send_file\n\napp = Flask(__name__)\n\n@app.route('/iot/device/logs')\ndef fetch_device_logs():\n    device_id = request.args.get('device_id', 'default')\n    log_filename = request.args.get('log_file', 'status.log')\n    \n    # Reject inputs containing path traversal characters\n    if '..' in device_id or '/' in device_id or '\\\\' in device_id:\n        return {'error': 'Invalid device ID'}, 400\n    if '..' in log_filename or '/' in log_filename or '\\\\' in log_filename:\n        return {'error': 'Invalid log filename'}, 400\n    \n    base_dir = Path('/var/iot/devices').resolve()\n    log_path = (base_dir / device_id / log_filename).resolve()\n    \n    # Ensure the resolved path is within the base directory\n    if not str(log_path).startswith(str(base_dir) + '/'):\n        return {'error': 'Access denied'}, 403\n    \n    if log_path.exists():\n        return send_file(str(log_path), mimetype='text/plain')\n    return {'error': 'Log file not found'}, 404"}