{"title":"XML Entity Expansion (Billion Laughs) via lxml Parser Misconfiguration","language":"Python","severity":"High","cwe":"CWE-611","source_lines":[3],"flow_lines":[3,8],"sink_lines":[8],"vulnerable_code":"from lxml import etree\nimport boto3\n\ndef process_iot_device_telemetry(xml_payload, device_id):\n    s3_client = boto3.client('s3')\n    parser = etree.XMLParser(resolve_entities=True, no_network=False)\n    try:\n        telemetry_doc = etree.fromstring(xml_payload.encode(), parser)\n        sensor_data = {elem.tag: elem.text for elem in telemetry_doc.findall('.//*')}\n        metadata = f\"Device: {device_id}, Readings: {len(sensor_data)}\"\n        s3_client.put_object(Bucket='iot-telemetry-lake', Key=f'devices/{device_id}/latest.json', Body=str(sensor_data))\n        return {'status': 'processed', 'device': device_id, 'metrics': sensor_data}\n    except etree.XMLSyntaxError as e:\n        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":"from lxml import etree\nimport boto3\n\ndef process_iot_device_telemetry(xml_payload, device_id):\n    s3_client = boto3.client('s3')\n    parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False, huge_tree=False)\n    try:\n        telemetry_doc = etree.fromstring(xml_payload.encode(), parser)\n        sensor_data = {elem.tag: elem.text for elem in telemetry_doc.findall('.//*')}\n        metadata = f\"Device: {device_id}, Readings: {len(sensor_data)}\"\n        s3_client.put_object(Bucket='iot-telemetry-lake', Key=f'devices/{device_id}/latest.json', Body=str(sensor_data))\n        return {'status': 'processed', 'device': device_id, 'metrics': sensor_data}\n    except etree.XMLSyntaxError as e:\n        return {'status': 'error', 'message': str(e)}"}