# Billion Laughs XML Entity Expansion DoS via lxml.etree.fromstring

Language: Python
Severity: High
CWE: CWE-776

## Source
5

## Flow
5-6

## Sink
6

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

def process_iot_device_telemetry(encoded_payload):
    raw_xml = base64.b64decode(encoded_payload)
    telemetry_doc = etree.fromstring(raw_xml)
    device_id = telemetry_doc.find('.//deviceID').text
    metrics = {elem.tag: elem.text for elem in telemetry_doc.findall('.//metric')}
    temperature = metrics.get('temp', 'N/A')
    humidity = metrics.get('humidity', 'N/A')
    return {'device': device_id, 'temp': temperature, 'humidity': humidity}
```

## Explanation

The code decodes base64-encoded XML and parses it using lxml.etree.fromstring() without disabling entity expansion. An attacker can send XML containing recursive entity definitions (Billion Laughs attack) that exponentially expand in memory, causing denial of service through resource exhaustion.

## Remediation

The fix creates a custom XMLParser with resolve_entities=False to prevent entity expansion, no_network=True to block external network access, and load_dtd=False to prevent loading of DTD definitions. This neutralizes Billion Laughs attacks by refusing to expand recursive entity definitions in the XML payload.

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

def process_iot_device_telemetry(encoded_payload):
    raw_xml = base64.b64decode(encoded_payload)
    parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)
    telemetry_doc = etree.fromstring(raw_xml, parser=parser)
    device_id = telemetry_doc.find('.//deviceID').text
    metrics = {elem.tag: elem.text for elem in telemetry_doc.findall('.//metric')}
    temperature = metrics.get('temp', 'N/A')
    humidity = metrics.get('humidity', 'N/A')
    return {'device': device_id, 'temp': temperature, 'humidity': humidity}
```
