# XML Entity Expansion (Billion Laughs) via lxml Parser Misconfiguration

Language: Python
Severity: High
CWE: CWE-611

## Source
3

## Flow
3-8

## Sink
8

## Vulnerable Code
```python
from lxml import etree
import boto3

def process_iot_device_telemetry(xml_payload, device_id):
    s3_client = boto3.client('s3')
    parser = etree.XMLParser(resolve_entities=True, no_network=False)
    try:
        telemetry_doc = etree.fromstring(xml_payload.encode(), parser)
        sensor_data = {elem.tag: elem.text for elem in telemetry_doc.findall('.//*')}
        metadata = f"Device: {device_id}, Readings: {len(sensor_data)}"
        s3_client.put_object(Bucket='iot-telemetry-lake', Key=f'devices/{device_id}/latest.json', Body=str(sensor_data))
        return {'status': 'processed', 'device': device_id, 'metrics': sensor_data}
    except etree.XMLSyntaxError as e:
        return {'status': 'error', 'message': str(e)}
```

## Explanation

The function accepts untrusted XML payload from IoT devices and parses it with lxml using resolve_entities=True and no_network=False. This configuration allows XML Entity Expansion (Billion Laughs) and XXE attacks, where malicious XML can define recursive entities that exponentially expand in memory, causing DoS, or reference external entities to exfiltrate data.

## Remediation

The fix configures the lxml XMLParser with secure settings: resolve_entities=False prevents entity expansion (blocking Billion Laughs attacks), no_network=True prevents fetching external resources, and dtd_validation=False with load_dtd=False ensures DTDs are neither loaded nor processed. Additionally, huge_tree=False enforces parser limits to prevent memory exhaustion from oversized documents.

## Secure Code
```python
from lxml import etree
import boto3

def process_iot_device_telemetry(xml_payload, device_id):
    s3_client = boto3.client('s3')
    parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False, huge_tree=False)
    try:
        telemetry_doc = etree.fromstring(xml_payload.encode(), parser)
        sensor_data = {elem.tag: elem.text for elem in telemetry_doc.findall('.//*')}
        metadata = f"Device: {device_id}, Readings: {len(sensor_data)}"
        s3_client.put_object(Bucket='iot-telemetry-lake', Key=f'devices/{device_id}/latest.json', Body=str(sensor_data))
        return {'status': 'processed', 'device': device_id, 'metrics': sensor_data}
    except etree.XMLSyntaxError as e:
        return {'status': 'error', 'message': str(e)}
```
