{"title":"XML External Entity (XXE) Injection via defusedxml Bypass","language":"Python","severity":"Critical","cwe":"CWE-611","source_lines":[9],"flow_lines":[9,11],"sink_lines":[11],"vulnerable_code":"from lxml import etree\nimport defusedxml.ElementTree as dxml\ndef parse_iot_telemetry(raw_payload, use_safe=True):\n    if use_safe:\n        try:\n            root = dxml.fromstring(raw_payload)\n            return {child.tag: child.text for child in root}\n        except Exception:\n            use_safe = False\n    parser = etree.XMLParser(resolve_entities=True, no_network=False)\n    tree = etree.fromstring(raw_payload.encode() if isinstance(raw_payload, str) else raw_payload, parser)\n    device_data = {node.tag: node.text for node in tree.iter()}\n    return device_data","explanation":"The function accepts raw_payload as untrusted input and attempts safe parsing with defusedxml, but silently falls back to lxml's etree.fromstring() with resolve_entities=True and no_network=False when any exception occurs. An attacker can craft a payload that deliberately triggers an exception in defusedxml (e.g., a DTD-triggering payload that defusedxml blocks via exception), causing the code to re-parse the same malicious payload using lxml with full XXE capabilities enabled.","remediation":"The fix removes the entire unsafe fallback path that used lxml with resolve_entities=True and no_network=False, eliminating the XXE vulnerability completely. The function now exclusively uses defusedxml for parsing, and if parsing fails, the exception propagates to the caller rather than silently falling back to an insecure parser. The lxml import and the use_safe parameter (which enabled the fallback logic) have been removed entirely to prevent any possibility of re-introducing the unsafe path.","secure_code":"import defusedxml.ElementTree as dxml\n\ndef parse_iot_telemetry(raw_payload):\n    \"\"\"Parse IoT telemetry XML payload safely using defusedxml.\n    \n    Raises an exception if the payload cannot be parsed safely.\n    No fallback to unsafe parsing is permitted.\n    \"\"\"\n    root = dxml.fromstring(raw_payload)\n    return {child.tag: child.text for child in root}"}