{"title":"Template Injection via Untrusted f-string Evaluation","language":"Python","severity":"Critical","cwe":"CWE-94","source_lines":[7,8],"flow_lines":[7,8,9,10],"sink_lines":[10],"vulnerable_code":"from flask import Flask, request\nimport boto3\n\napp = Flask(__name__)\ns3_client = boto3.client('s3')\n\n@app.route('/cloud/provision')\ndef provision_s3_bucket():\n    tenant_id = request.args.get('tenant', 'default')\n    region_pref = request.args.get('region', 'us-east-1')\n    bucket_template = f\"s3-bucket-{tenant_id}-{region_pref}-data\"\n    bucket_name = eval(f\"f'{bucket_template}'\")\n    s3_client.create_bucket(Bucket=bucket_name)\n    return {\"status\": \"provisioned\", \"bucket\": bucket_name}","explanation":"User-controlled parameters 'tenant' and 'region' from request.args are incorporated into an f-string template, which is then passed to eval() for dynamic evaluation. This allows attackers to inject arbitrary Python code through these parameters, leading to remote code execution when the f-string is evaluated.","remediation":"The fix removes the dangerous eval() call entirely and constructs the bucket name using a simple f-string directly. Additionally, strict input validation is applied: tenant_id is restricted to alphanumeric characters and hyphens via regex, and region is validated against an allowlist of permitted AWS regions. This prevents any code injection while still providing the intended functionality.","secure_code":"import re\nfrom flask import Flask, request\nimport boto3\n\napp = Flask(__name__)\ns3_client = boto3.client('s3')\n\nALLOWED_REGIONS = ['us-east-1', 'us-east-2', 'us-west-1', 'us-west-2', 'eu-west-1', 'eu-central-1']\nTENANT_ID_PATTERN = re.compile(r'^[a-zA-Z0-9\\-]{1,50}$')\n\n@app.route('/cloud/provision')\ndef provision_s3_bucket():\n    tenant_id = request.args.get('tenant', 'default')\n    region_pref = request.args.get('region', 'us-east-1')\n\n    if not TENANT_ID_PATTERN.match(tenant_id):\n        return {\"status\": \"error\", \"message\": \"Invalid tenant ID. Only alphanumeric characters and hyphens are allowed.\"}, 400\n\n    if region_pref not in ALLOWED_REGIONS:\n        return {\"status\": \"error\", \"message\": f\"Invalid region. Allowed regions: {ALLOWED_REGIONS}\"}, 400\n\n    bucket_name = f\"s3-bucket-{tenant_id}-{region_pref}-data\"\n    s3_client.create_bucket(Bucket=bucket_name)\n    return {\"status\": \"provisioned\", \"bucket\": bucket_name}"}