# Template Injection via Jinja2 render_template_string()

Language: Python
Severity: Critical
CWE: CWE-1336

## Source
9, 12

## Flow
9-13-14, 12-16-17

## Sink
14, 17

## Vulnerable Code
```python
from flask import Flask, request, render_template_string
import boto3

app = Flask(__name__)
s3_client = boto3.client('s3')

@app.route('/cloud/bucket-report')
def generate_bucket_report():
    bucket_name = request.args.get('bucket', 'default-bucket')
    report_format = request.args.get('format', 'html')
    user_title = request.args.get('title', 'S3 Bucket Report')
    try:
        response = s3_client.list_objects_v2(Bucket=bucket_name, MaxKeys=5)
        object_count = response.get('KeyCount', 0)
        report_template = f"<html><head><title>{user_title}</title></head><body><h1>{user_title}</h1><p>Bucket: {bucket_name}</p><p>Objects found: {object_count}</p></body></html>"
        return render_template_string(report_template)
    except Exception as e:
        error_msg = request.args.get('error_template', 'Error accessing bucket')
        error_page = f"<html><body><h2>Cloud Storage Error</h2><p>{error_msg}</p><p>Details: {str(e)}</p></body></html>"
        return render_template_string(error_page), 500
```

## Explanation

User-controlled input from request.args.get('title') and request.args.get('error_template') is directly interpolated into f-strings and then passed to render_template_string(), which treats them as Jinja2 templates. This allows attackers to inject arbitrary Jinja2 template expressions that execute on the server.

## Remediation

The fix replaces direct f-string interpolation of user input into template strings with Jinja2's built-in variable substitution using {{ }} placeholders and passing user data as context parameters to render_template_string(). This ensures all user-controlled values are automatically escaped by Jinja2's autoescaping and are never interpreted as template code.

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

app = Flask(__name__)
s3_client = boto3.client('s3')

@app.route('/cloud/bucket-report')
def generate_bucket_report():
    bucket_name = request.args.get('bucket', 'default-bucket')
    report_format = request.args.get('format', 'html')
    user_title = request.args.get('title', 'S3 Bucket Report')
    try:
        response = s3_client.list_objects_v2(Bucket=bucket_name, MaxKeys=5)
        object_count = response.get('KeyCount', 0)
        report_template = "<html><head><title>{{ title }}</title></head><body><h1>{{ title }}</h1><p>Bucket: {{ bucket }}</p><p>Objects found: {{ count }}</p></body></html>"
        return render_template_string(report_template, title=user_title, bucket=bucket_name, count=object_count)
    except Exception as e:
        error_msg = request.args.get('error_template', 'Error accessing bucket')
        error_page = "<html><body><h2>Cloud Storage Error</h2><p>{{ error_msg }}</p><p>Details: {{ details }}</p></body></html>"
        return render_template_string(error_page, error_msg=error_msg, details=str(e)), 500
```
