{"title":"Server-Side Template Injection (SSTI)","language":"Python","severity":"Critical","cwe":"CWE-94","source_lines":[5,6],"flow_lines":[5,6,7,8],"sink_lines":[8],"vulnerable_code":"from flask import Flask, request, render_template_string\napp = Flask(__name__)\n@app.route('/device/status')\ndef iot_device_status():\n    dev_id = request.args.get('device_id', 'unknown')\n    fw_ver = request.args.get('firmware', 'N/A')\n    tpl = f'<h2>Device: {dev_id}</h2><p>Firmware: {fw_ver}</p><p>Status: Active</p>'\n    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":"from flask import Flask, request, render_template_string\nfrom markupsafe import escape\n\napp = Flask(__name__)\n\n@app.route('/device/status')\ndef iot_device_status():\n    dev_id = request.args.get('device_id', 'unknown')\n    fw_ver = request.args.get('firmware', 'N/A')\n    tpl = '<h2>Device: {{ dev_id }}</h2><p>Firmware: {{ fw_ver }}</p><p>Status: Active</p>'\n    return render_template_string(tpl, dev_id=dev_id, fw_ver=fw_ver)"}