# Path Traversal via Untrusted File Open Path Joins

Language: Python
Severity: High
CWE: CWE-22

## Source
8, 9

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

## Sink
12

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

app = Flask(__name__)

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

## Explanation

The application constructs a file path using user-controlled parameters 'model' and 'ver' without validation, allowing path traversal attacks. An attacker can use directory traversal sequences (../) in these parameters to escape the intended firmware_base directory and access arbitrary files on the server filesystem.

## Remediation

The fix applies a two-layer defense: first, it validates that both user-supplied parameters (model and version) contain only safe alphanumeric characters, hyphens, underscores, and dots using a whitelist regex, rejecting any path traversal characters. Second, it resolves the constructed path to its canonical absolute form using os.path.realpath() and verifies the resolved path remains within the intended firmware base directory, preventing any bypass of the input validation.

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

app = Flask(__name__)

@app.route('/iot/firmware/download')
def retrieve_firmware_binary():
    device_model = request.args.get('model', 'default')
    fw_version = request.args.get('ver', '1.0')
    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 'Invalid model parameter', 400
    if not re.match(r'^[a-zA-Z0-9._-]+$', fw_version):
        return 'Invalid version parameter', 400

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

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

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