# Server-Side Template Injection (SSTI)

Language: Python
Severity: Critical
CWE: CWE-94

## Source
5-6

## Flow
5-6-7-8

## Sink
8

## Vulnerable Code
```python
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/device/status')
def iot_device_status():
    dev_id = request.args.get('device_id', 'unknown')
    fw_ver = request.args.get('firmware', 'N/A')
    tpl = f'<h2>Device: {dev_id}</h2><p>Firmware: {fw_ver}</p><p>Status: Active</p>'
    return render_template_string(tpl)
```

## Explanation

User-controlled query parameters 'device_id' and 'firmware' (lines 5-6) are directly interpolated via f-string into a Jinja2 template string (line 7), which is then passed to render_template_string() (line 8). Because the attacker-controlled values are embedded into the template before rendering, Jinja2 will evaluate any injected expressions such as {{7*7}} or {{config}} as template code, enabling SSTI and potentially full Remote Code Execution.

## Remediation

The fix eliminates the f-string interpolation of user input into the template string. Instead, the template uses Jinja2 placeholder variables ({{ dev_id }} and {{ fw_ver }}), and the user-controlled values are passed as context parameters to render_template_string(). Jinja2 automatically escapes these context variables during rendering, preventing any injected template expressions from being evaluated as code.

## Secure Code
```python
from flask import Flask, request, render_template_string
from markupsafe import escape

app = Flask(__name__)

@app.route('/device/status')
def iot_device_status():
    dev_id = request.args.get('device_id', 'unknown')
    fw_ver = request.args.get('firmware', 'N/A')
    tpl = '<h2>Device: {{ dev_id }}</h2><p>Firmware: {{ fw_ver }}</p><p>Status: Active</p>'
    return render_template_string(tpl, dev_id=dev_id, fw_ver=fw_ver)
```
