# Server-Side Template Injection (SSTI)

Language: Python
Severity: Critical
CWE: CWE-94

## Source
9

## Flow
9-11

## Sink
11

## Vulnerable Code
```python
from flask import Flask, request
from jinja2 import Environment
app = Flask(__name__)
jinja_env = Environment()
@app.route('/iot/device/status')
def fetch_device_status():
    dev_label = request.args.get('label', 'unknown')
    tmpl_str = 'Device Report: {{ ' + dev_label + ' }}'
    compiled_tmpl = jinja_env.from_string(tmpl_str)
    rendered_output = compiled_tmpl.render(uptime=9999, temp=72.4)
    return rendered_output
```

## Explanation

The user-supplied 'label' query parameter (dev_label) is directly concatenated into a Jinja2 template string at line 8 without any sanitization or escaping. This template string is then compiled and rendered by jinja_env.from_string() at line 9, allowing an attacker to inject arbitrary Jinja2 template expressions that are evaluated server-side, potentially leading to remote code execution.

## Remediation

The fix eliminates the SSTI vulnerability by never concatenating user input into the Jinja2 template string. Instead, the user-supplied label is validated against a whitelist of allowed device field names, and the resolved value is passed into the template context as a data variable. Additionally, the raw Jinja2 Environment is replaced with SandboxedEnvironment as a defense-in-depth measure to restrict template capabilities even if future template logic changes.

## Secure Code
```python
from flask import Flask, request
from jinja2 import Environment, SandboxedEnvironment
app = Flask(__name__)
jinja_env = SandboxedEnvironment()

# Whitelist of allowed device label names
ALLOWED_LABELS = {'uptime', 'temp', 'humidity', 'pressure', 'voltage', 'status'}

@app.route('/iot/device/status')
def fetch_device_status():
    dev_label = request.args.get('label', 'unknown')
    # Validate that the label is in the allowed set of known device fields
    if dev_label not in ALLOWED_LABELS:
        dev_label = 'unknown'
    # Use the label as a safe variable reference passed via the template context,
    # never concatenating user input into the template string itself.
    tmpl_str = 'Device Report: {{ device_label }}'
    compiled_tmpl = jinja_env.from_string(tmpl_str)
    device_data = {'uptime': 9999, 'temp': 72.4, 'humidity': 55.0, 'pressure': 1013, 'voltage': 3.3, 'status': 'online'}
    label_value = device_data.get(dev_label, dev_label)
    rendered_output = compiled_tmpl.render(device_label=label_value)
    return rendered_output
```
