# Path Traversal via unsanitized os.path.join() filename input

Language: Python
Severity: High
CWE: CWE-22

## Source
9

## Flow
9-10-11

## Sink
11

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

app = Flask(__name__)
MODEL_STORAGE = '/var/ml/trained_models'

@app.route('/api/v1/model/download', methods=['GET'])
def fetch_trained_model():
    model_identifier = request.args.get('model_id', 'default.h5')
    model_path = os.path.join(MODEL_STORAGE, model_identifier)
    if os.path.exists(model_path):
        return send_file(model_path, as_attachment=True)
    return {'error': 'Model not found'}, 404
```

## Explanation

The vulnerability exists because user-controlled input from 'model_id' parameter is directly concatenated into a file path using os.path.join() without validation, then passed to send_file(). An attacker can inject path traversal sequences (../) to access arbitrary files outside the intended MODEL_STORAGE directory.

## Remediation

The fix applies two layers of defense: first, os.path.basename() strips any directory traversal components (like '../') from the user input, keeping only the filename portion. Second, os.path.realpath() resolves the final path and verifies it still resides within the intended MODEL_STORAGE directory, preventing any symlink or edge-case bypass.

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

app = Flask(__name__)
MODEL_STORAGE = '/var/ml/trained_models'

@app.route('/api/v1/model/download', methods=['GET'])
def fetch_trained_model():
    model_identifier = request.args.get('model_id', 'default.h5')
    # Sanitize: extract only the basename to prevent directory traversal
    safe_filename = os.path.basename(model_identifier)
    if not safe_filename:
        return {'error': 'Invalid model identifier'}, 400
    model_path = os.path.join(MODEL_STORAGE, safe_filename)
    # Verify the resolved path is still within the allowed directory
    real_model_path = os.path.realpath(model_path)
    real_storage_dir = os.path.realpath(MODEL_STORAGE)
    if not real_model_path.startswith(real_storage_dir + os.sep):
        return {'error': 'Invalid model identifier'}, 400
    if os.path.exists(real_model_path):
        return send_file(real_model_path, as_attachment=True)
    return {'error': 'Model not found'}, 404
```
