# Path Traversal via pathlib.resolve on Untrusted Filename

Language: Python
Severity: High
CWE: CWE-22

## Source
8

## Flow
8-10-11

## Sink
11

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

app = Flask(__name__)

@app.route('/api/v1/download_model')
def fetch_trained_model():
    model_name = request.args.get('model_id', 'default.h5')
    models_dir = Path('/opt/ml/models')
    target_model = (models_dir / model_name).resolve()
    if target_model.exists():
        return send_file(target_model, as_attachment=True)
    return {'error': 'Model not found'}, 404
```

## Explanation

The application accepts user-controlled input via 'model_id' parameter without validation and constructs a file path using pathlib. While resolve() canonicalizes the path, there's no verification that the resolved path remains within the intended '/opt/ml/models' directory, allowing path traversal attacks to access arbitrary files on the system.

## Remediation

The fix adds a path containment check using `is_relative_to()` after resolving the path, which verifies that the resolved target path remains within the intended '/opt/ml/models' directory. If the resolved path escapes the allowed directory (e.g., via '../' sequences), the request is rejected with a 400 error before any file access occurs. The models_dir is also resolved first to ensure consistent comparison.

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

app = Flask(__name__)

@app.route('/api/v1/download_model')
def fetch_trained_model():
    model_name = request.args.get('model_id', 'default.h5')
    models_dir = Path('/opt/ml/models').resolve()
    target_model = (models_dir / model_name).resolve()
    if not target_model.is_relative_to(models_dir):
        return {'error': 'Invalid model identifier'}, 400
    if target_model.exists():
        return send_file(target_model, as_attachment=True)
    return {'error': 'Model not found'}, 404
```
