{"title":"XML Billion Laughs Denial of Service via Entity Expansion","language":"Python","severity":"High","cwe":"CWE-776","source_lines":[7],"flow_lines":[7,8],"sink_lines":[8],"vulnerable_code":"import xml.etree.ElementTree as ET\nfrom flask import Flask, request, jsonify\n\niot_app = Flask(__name__)\n\n@iot_app.route('/device/telemetry', methods=['POST'])\ndef ingest_sensor_telemetry():\n    telemetry_payload = request.data\n    device_tree = ET.fromstring(telemetry_payload)\n    sensor_readings = {}\n    for metric in device_tree.findall('.//reading'):\n        sensor_readings[metric.get('type')] = metric.text\n    return jsonify({'status': 'processed', 'metrics': sensor_readings})","explanation":"The endpoint receives untrusted XML data via request.data and directly parses it using ET.fromstring() without disabling external entity processing or entity expansion. This allows an attacker to craft a malicious XML payload with recursive entity definitions (Billion Laughs attack) that exponentially expands in memory, causing denial of service.","remediation":"The fix replaces the standard `xml.etree.ElementTree` module with `defusedxml.ElementTree`, which automatically prevents XML entity expansion attacks (Billion Laughs), external entity processing (XXE), and other XML-based attacks. Additionally, error handling was added to gracefully handle malformed or malicious XML payloads instead of crashing the application.","secure_code":"import defusedxml.ElementTree as ET\nfrom flask import Flask, request, jsonify\n\niot_app = Flask(__name__)\n\n@iot_app.route('/device/telemetry', methods=['POST'])\ndef ingest_sensor_telemetry():\n    telemetry_payload = request.data\n    try:\n        device_tree = ET.fromstring(telemetry_payload)\n    except ET.ParseError:\n        return jsonify({'status': 'error', 'message': 'Invalid XML payload'}), 400\n    except Exception as e:\n        return jsonify({'status': 'error', 'message': 'XML processing error'}), 400\n    sensor_readings = {}\n    for metric in device_tree.findall('.//reading'):\n        sensor_readings[metric.get('type')] = metric.text\n    return jsonify({'status': 'processed', 'metrics': sensor_readings})"}