# Unsafe XML Parsing via xml.etree.ElementTree on Untrusted Input (Billion Laughs / DTD Entity Expansion DoS)

Language: Python
Severity: High
CWE: CWE-611

## Source
8

## Flow
8-9

## Sink
9

## Vulnerable Code
```python
import xml.etree.ElementTree as ET
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/iot/device/telemetry', methods=['POST'])
def process_device_telemetry():
    telemetry_xml = request.data.decode('utf-8')
    device_tree = ET.fromstring(telemetry_xml)
    device_id = device_tree.find('deviceId').text
    metrics = {metric.tag: metric.text for metric in device_tree.find('metrics')}
    return jsonify({'status': 'processed', 'device': device_id, 'data': metrics})
```

## Explanation

The endpoint accepts untrusted XML data from IoT devices and parses it directly using ET.fromstring() without any input validation or use of a hardened XML parser. While modern CPython's xml.etree.ElementTree does not resolve external file entities (limiting classic XXE file-disclosure), it remains vulnerable to entity expansion (Billion Laughs) denial-of-service attacks through recursive or deeply nested entity definitions embedded in DTDs. Parsing attacker-controlled XML without defenses can exhaust server memory and CPU, causing service disruption.

## Remediation

The fix replaces the vulnerable xml.etree.ElementTree module with defusedxml.ElementTree, a drop-in replacement that automatically disables DTD processing, entity expansion, and other dangerous XML features. This prevents entity-expansion DoS attacks and provides defense-in-depth against any future parser-level changes that might re-enable external entity resolution, while maintaining the same API interface.

## Secure Code
```python
import defusedxml.ElementTree as ET
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/iot/device/telemetry', methods=['POST'])
def process_device_telemetry():
    telemetry_xml = request.data.decode('utf-8')
    device_tree = ET.fromstring(telemetry_xml)
    device_id = device_tree.find('deviceId').text
    metrics = {metric.tag: metric.text for metric in device_tree.find('metrics')}
    return jsonify({'status': 'processed', 'device': device_id, 'data': metrics})
```
