# Jinja2 Template Injection via render_template_string()

Language: Python
Severity: Critical
CWE: CWE-94

## Source
8-9

## Flow
8-9-10-11

## Sink
11

## 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-policy')
def generate_bucket_policy():
    bucket_name = request.args.get('bucket', 'default-bucket')
    policy_template = request.args.get('policy_desc', 'Standard access policy')
    policy_html = f"<h2>S3 Bucket: {bucket_name}</h2><p>Policy: {policy_template}</p>"
    return render_template_string(policy_html)
```

## Explanation

User-controlled input from request parameters (bucket_name and policy_template) is directly embedded into an HTML string via f-string formatting, then passed to render_template_string() without sanitization. This allows attackers to inject Jinja2 template expressions that will be evaluated server-side, leading to remote code execution.

## Remediation

The fix replaces direct f-string interpolation of user input into the template string with Jinja2 template variables ({{ bucket_name }} and {{ policy_desc }}). By passing user-controlled values as context parameters to render_template_string(), Jinja2 automatically escapes them, preventing both template injection and XSS attacks since the template structure itself is now a static string that cannot be manipulated by user input.

## 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-policy')
def generate_bucket_policy():
    bucket_name = request.args.get('bucket', 'default-bucket')
    policy_template = request.args.get('policy_desc', 'Standard access policy')
    policy_html = "<h2>S3 Bucket: {{ bucket_name }}</h2><p>Policy: {{ policy_desc }}</p>"
    return render_template_string(policy_html, bucket_name=bucket_name, policy_desc=policy_template)
```
