# Path Traversal via Unsanitized File Read in pathlib.Path.resolve()

Language: Python
Severity: High
CWE: CWE-22

## Source
8, 9

## Flow
8-11-12-13, 9-11-12-13

## Sink
13

## Vulnerable Code
```python
from pathlib import Path
from flask import Flask, request, send_file

app = Flask(__name__)

@app.route('/iot/device/logs')
def fetch_device_logs():
    device_id = request.args.get('device_id', 'default')
    log_filename = request.args.get('log_file', 'status.log')
    base_dir = Path('/var/iot/devices')
    log_path = base_dir / device_id / log_filename
    resolved = log_path.resolve()
    if resolved.exists():
        return send_file(str(resolved), mimetype='text/plain')
    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
```python
from pathlib import Path
from flask import Flask, request, send_file

app = Flask(__name__)

@app.route('/iot/device/logs')
def fetch_device_logs():
    device_id = request.args.get('device_id', 'default')
    log_filename = request.args.get('log_file', 'status.log')
    
    # Reject inputs containing path traversal characters
    if '..' in device_id or '/' in device_id or '\\' in device_id:
        return {'error': 'Invalid device ID'}, 400
    if '..' in log_filename or '/' in log_filename or '\\' in log_filename:
        return {'error': 'Invalid log filename'}, 400
    
    base_dir = Path('/var/iot/devices').resolve()
    log_path = (base_dir / device_id / log_filename).resolve()
    
    # Ensure the resolved path is within the base directory
    if not str(log_path).startswith(str(base_dir) + '/'):
        return {'error': 'Access denied'}, 403
    
    if log_path.exists():
        return send_file(str(log_path), mimetype='text/plain')
    return {'error': 'Log file not found'}, 404
```
