# Path Traversal via unvalidated pathlib.Path.joinpath()

Language: Python
Severity: Critical
CWE: CWE-22

## Source
9

## Flow
9-10

## Sink
10

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

app = Flask(__name__)
HSM_KEY_VAULT = Path("/secure/hsm/keystore")

@app.route("/api/hsm/export-key")
def export_cryptographic_key():
    key_identifier = request.args.get("key_id", "default")
    key_file_path = HSM_KEY_VAULT.joinpath(key_identifier)
    if key_file_path.exists():
        return send_file(key_file_path, as_attachment=True)
    return {"error": "Key not found"}, 404
```

## Explanation

The application accepts a user-controlled 'key_id' parameter and directly passes it to pathlib.Path.joinpath() without validation. An attacker can use path traversal sequences (e.g., '../../../etc/passwd') to escape the intended HSM_KEY_VAULT directory and access arbitrary files on the filesystem, which are then served via send_file().

## Remediation

The fix resolves the constructed path to its absolute canonical form using .resolve(), then validates that the resolved path is still within the intended HSM_KEY_VAULT directory using .is_relative_to(). This prevents path traversal attacks because any '../' sequences are resolved before the containment check, ensuring the final path cannot escape the designated keystore directory.

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

app = Flask(__name__)
HSM_KEY_VAULT = Path("/secure/hsm/keystore")

@app.route("/api/hsm/export-key")
def export_cryptographic_key():
    key_identifier = request.args.get("key_id", "default")
    key_file_path = HSM_KEY_VAULT.joinpath(key_identifier).resolve()
    if not key_file_path.is_relative_to(HSM_KEY_VAULT.resolve()):
        return {"error": "Invalid key identifier"}, 400
    if key_file_path.exists():
        return send_file(key_file_path, as_attachment=True)
    return {"error": "Key not found"}, 404
```
