# Format String Injection via str.format() on Untrusted Template Fragments

Language: Python
Severity: High
CWE: CWE-134

## Source
1

## Flow
1-2-9

## Sink
9

## Vulnerable Code
```python
def generate_iot_device_alert(device_id, sensor_reading, alert_template):
    device_metadata = {
        'device_id': device_id,
        'reading': sensor_reading,
        'timestamp': '2024-01-15T10:30:00Z',
        'api_key': 'sk_prod_9x8c7v6b5n4m3',
        'internal_ip': '10.0.2.15'
    }
    notification_msg = alert_template.format(**device_metadata)
    send_to_monitoring_dashboard(notification_msg)
    return notification_msg

def send_to_monitoring_dashboard(msg):
    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
```python
import re

ALLOWED_FIELDS = {'device_id', 'reading', 'timestamp'}

def generate_iot_device_alert(device_id, sensor_reading, alert_template):
    device_metadata = {
        'device_id': device_id,
        'reading': sensor_reading,
        'timestamp': '2024-01-15T10:30:00Z',
        'api_key': 'sk_prod_9x8c7v6b5n4m3',
        'internal_ip': '10.0.2.15'
    }
    
    # Validate template only references allowed fields
    referenced_fields = set(re.findall(r'\{(\w+)(?:[^}]*)?\}', alert_template))
    disallowed_fields = referenced_fields - ALLOWED_FIELDS
    if disallowed_fields:
        raise ValueError(f"Template references disallowed fields: {disallowed_fields}")
    
    # Reject any format specifiers that could enable attribute/index traversal
    if re.search(r'\{[^}]*(\.|\[|__|!)', alert_template):
        raise ValueError("Template contains disallowed format specifiers")
    
    # Only pass safe, public fields to format
    safe_metadata = {key: device_metadata[key] for key in ALLOWED_FIELDS}
    notification_msg = alert_template.format(**safe_metadata)
    send_to_monitoring_dashboard(notification_msg)
    return notification_msg

def send_to_monitoring_dashboard(msg):
    print(f"[ALERT SENT] {msg}")
```
