# Path Traversal via os.path.join with User-Controlled Absolute Path Override

Language: Python
Severity: High
CWE: CWE-22

## Source
6

## Flow
6-7

## Sink
7

## Vulnerable Code
```python
import os
from flask import request, send_file

def retrieve_iot_device_logs(device_id):
    log_directory = "/var/iot/device_logs/"
    requested_log = request.args.get('log_file', 'system.log')
    log_path = os.path.join(log_directory, device_id, requested_log)
    if os.path.exists(log_path):
        return send_file(log_path, as_attachment=True)
    return {"error": "Log file not found"}, 404
```

## Explanation

The vulnerability exists because os.path.join() allows an absolute path in the 'requested_log' parameter to override the intended base directory. When a user provides an absolute path like '/etc/passwd', os.path.join ignores all preceding path components, allowing arbitrary file access. The tainted input from request.args.get() flows directly into os.path.join() and then to send_file() without validation.

## Remediation

The fix applies multiple layers of defense: first, os.path.basename() strips any directory components (including absolute paths and '../' sequences) from both device_id and requested_log, ensuring only filenames are used. Second, os.path.realpath() resolves the final path and a prefix check confirms the resolved path remains within the allowed log directory, preventing any bypass through symlinks or edge cases.

## Secure Code
```python
import os
from flask import request, send_file

def retrieve_iot_device_logs(device_id):
    log_directory = "/var/iot/device_logs/"
    requested_log = request.args.get('log_file', 'system.log')
    
    # Sanitize device_id and requested_log to prevent path traversal
    safe_device_id = os.path.basename(device_id)
    safe_log_file = os.path.basename(requested_log)
    
    # Construct the path using sanitized components
    log_path = os.path.join(log_directory, safe_device_id, safe_log_file)
    
    # Resolve to absolute path and verify it stays within the allowed directory
    log_path = os.path.realpath(log_path)
    allowed_directory = os.path.realpath(log_directory)
    
    if not log_path.startswith(allowed_directory + os.sep):
        return {"error": "Access denied"}, 403
    
    if os.path.exists(log_path) and os.path.isfile(log_path):
        return send_file(log_path, as_attachment=True)
    return {"error": "Log file not found"}, 404
```
