{"title":"Format String Injection via str.format() on Untrusted Template Fragments","language":"Python","severity":"High","cwe":"CWE-134","source_lines":[1],"flow_lines":[1,2,9],"sink_lines":[9],"vulnerable_code":"def generate_iot_device_alert(device_id, sensor_reading, alert_template):\n    device_metadata = {\n        'device_id': device_id,\n        'reading': sensor_reading,\n        'timestamp': '2024-01-15T10:30:00Z',\n        'api_key': 'sk_prod_9x8c7v6b5n4m3',\n        'internal_ip': '10.0.2.15'\n    }\n    notification_msg = alert_template.format(**device_metadata)\n    send_to_monitoring_dashboard(notification_msg)\n    return notification_msg\n\ndef send_to_monitoring_dashboard(msg):\n    print(f\"[ALERT SENT] {msg}\")","explanation":"The function accepts untrusted 'alert_template' input and passes it directly to str.format() with dictionary unpacking (**device_metadata). An attacker can craft malicious format strings to access sensitive data like API keys and internal IPs through attribute access (e.g., {api_key}, {__init__.__globals__}) that are exposed in the device_metadata dictionary.","remediation":"The fix implements a whitelist of allowed template fields (device_id, reading, timestamp) and validates that the user-provided template only references these safe fields. It also rejects any format specifiers containing dots, brackets, or dunder patterns that could enable attribute traversal attacks. Finally, only the safe subset of metadata is passed to str.format(), ensuring sensitive data like API keys and internal IPs can never be exposed.","secure_code":"import re\n\nALLOWED_FIELDS = {'device_id', 'reading', 'timestamp'}\n\ndef generate_iot_device_alert(device_id, sensor_reading, alert_template):\n    device_metadata = {\n        'device_id': device_id,\n        'reading': sensor_reading,\n        'timestamp': '2024-01-15T10:30:00Z',\n        'api_key': 'sk_prod_9x8c7v6b5n4m3',\n        'internal_ip': '10.0.2.15'\n    }\n    \n    # Validate template only references allowed fields\n    referenced_fields = set(re.findall(r'\\{(\\w+)(?:[^}]*)?\\}', alert_template))\n    disallowed_fields = referenced_fields - ALLOWED_FIELDS\n    if disallowed_fields:\n        raise ValueError(f\"Template references disallowed fields: {disallowed_fields}\")\n    \n    # Reject any format specifiers that could enable attribute/index traversal\n    if re.search(r'\\{[^}]*(\\.|\\[|__|!)', alert_template):\n        raise ValueError(\"Template contains disallowed format specifiers\")\n    \n    # Only pass safe, public fields to format\n    safe_metadata = {key: device_metadata[key] for key in ALLOWED_FIELDS}\n    notification_msg = alert_template.format(**safe_metadata)\n    send_to_monitoring_dashboard(notification_msg)\n    return notification_msg\n\ndef send_to_monitoring_dashboard(msg):\n    print(f\"[ALERT SENT] {msg}\")"}