# Path Traversal via Unsanitized File Read in os.path.join

Language: Python
Severity: Critical
CWE: CWE-22

## Source
8, 9

## Flow
8-9-11

## Sink
11

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

app = Flask(__name__)

@app.route('/api/iot/firmware/download')
def retrieve_firmware_binary():
    device_model = request.args.get('model', 'default')
    fw_version = request.args.get('version', 'latest')
    firmware_base = '/var/iot/firmware_repo'
    fw_path = os.path.join(firmware_base, device_model, fw_version, 'firmware.bin')
    if os.path.exists(fw_path):
        return send_file(fw_path, as_attachment=True)
    return {'error': 'Firmware not found'}, 404
```

## Explanation

User-controlled parameters 'model' and 'version' from request arguments are directly concatenated into a file path using os.path.join without any validation or sanitization. An attacker can inject path traversal sequences (../) to escape the intended firmware_base directory and access arbitrary files on the server filesystem.

## Remediation

The fix applies a defense-in-depth approach: first, it validates that both 'model' and 'version' parameters contain only safe alphanumeric characters (plus hyphens, underscores, and dots) using a whitelist regex. Second, it explicitly rejects any '..' sequences. Third, it resolves the final path using os.path.realpath() and verifies the resolved path starts with the intended firmware base directory, preventing any path traversal escape.

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

app = Flask(__name__)

@app.route('/api/iot/firmware/download')
def retrieve_firmware_binary():
    device_model = request.args.get('model', 'default')
    fw_version = request.args.get('version', 'latest')
    firmware_base = '/var/iot/firmware_repo'

    # Validate that model and version contain only safe characters (alphanumeric, hyphens, underscores, dots)
    if not re.match(r'^[a-zA-Z0-9_\-\.]+$', device_model):
        return {'error': 'Invalid device model'}, 400
    if not re.match(r'^[a-zA-Z0-9_\-\.]+$', fw_version):
        return {'error': 'Invalid firmware version'}, 400

    # Reject any path traversal components
    if '..' in device_model or '..' in fw_version:
        return {'error': 'Invalid parameters'}, 400

    fw_path = os.path.join(firmware_base, device_model, fw_version, 'firmware.bin')

    # Resolve to absolute path and verify it stays within the firmware base directory
    fw_real_path = os.path.realpath(fw_path)
    if not fw_real_path.startswith(os.path.realpath(firmware_base) + os.sep):
        return {'error': 'Access denied'}, 403

    if os.path.exists(fw_real_path):
        return send_file(fw_real_path, as_attachment=True)
    return {'error': 'Firmware not found'}, 404
```
