{"title":"Unsafe XML Parsing via xml.etree.ElementTree on Untrusted Input (Billion Laughs / DTD Entity Expansion DoS)","language":"Python","severity":"High","cwe":"CWE-611","source_lines":[8],"flow_lines":[8,9],"sink_lines":[9],"vulnerable_code":"import xml.etree.ElementTree as ET\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\n\n@app.route('/iot/device/telemetry', methods=['POST'])\ndef process_device_telemetry():\n    telemetry_xml = request.data.decode('utf-8')\n    device_tree = ET.fromstring(telemetry_xml)\n    device_id = device_tree.find('deviceId').text\n    metrics = {metric.tag: metric.text for metric in device_tree.find('metrics')}\n    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":"import defusedxml.ElementTree as ET\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\n\n@app.route('/iot/device/telemetry', methods=['POST'])\ndef process_device_telemetry():\n    telemetry_xml = request.data.decode('utf-8')\n    device_tree = ET.fromstring(telemetry_xml)\n    device_id = device_tree.find('deviceId').text\n    metrics = {metric.tag: metric.text for metric in device_tree.find('metrics')}\n    return jsonify({'status': 'processed', 'device': device_id, 'data': metrics})"}