{"title":"XML External Entity (XXE) Injection","language":"Python","severity":"Critical","cwe":"CWE-611","source_lines":[9],"flow_lines":[9,11],"sink_lines":[11],"vulnerable_code":"from xml.parsers import expat\ndef parse_iot_device_config(raw_xml_payload):\n    discovered_entities = {}\n    def handle_entity_decl(name, is_param, val, sysid, pubid, notation):\n        discovered_entities[name] = sysid or val\n    def capture_char_data(chunk):\n        parse_iot_device_config.last_data = getattr(parse_iot_device_config, 'last_data', '') + chunk\n    p = expat.ParserCreate()\n    p.EntityDeclHandler = handle_entity_decl\n    p.CharacterDataHandler = capture_char_data\n    p.ParseParamEntityExternals = 1\n    p.Parse(raw_xml_payload, 1)\n    return getattr(parse_iot_device_config, 'last_data', ''), discovered_entities","explanation":"The function accepts raw XML input (`raw_xml_payload`) from an untrusted source (IoT device agent over MQTT) and passes it directly to the expat parser with `ParseParamEntityExternals = 1` enabled. This setting explicitly allows the parser to resolve external parameter entities, enabling XXE attacks where an attacker can craft a malicious XML payload referencing external resources like local files (`/etc/passwd`) or internal network services. The `EntityDeclHandler` and `CharacterDataHandler` then capture and return the resolved entity content to the caller.","remediation":"The fix applies three defensive measures: (1) `SetParamEntityParsing` is set to `XML_PARAM_ENTITY_PARSING_NEVER` to prevent the parser from resolving external parameter entities, replacing the dangerous `ParseParamEntityExternals = 1` setting. (2) The `EntityDeclHandler` now explicitly rejects any entity declaration that includes a system ID or public ID (i.e., external entities), raising a `ValueError` to abort parsing. (3) Discovered external entities are no longer collected and returned, eliminating the data exfiltration path. The character data accumulation was also refactored to use a list instead of a mutable function attribute for cleaner design.","secure_code":"from xml.parsers import expat\n\n\ndef parse_iot_device_config(raw_xml_payload):\n    captured_data = []\n\n    def handle_entity_decl(name, is_param, val, sysid, pubid, notation):\n        # Reject any external entity declarations (those with a system or public ID)\n        if sysid is not None or pubid is not None:\n            raise ValueError(\n                f\"External entity declarations are not allowed: entity '{name}'\"\n            )\n\n    def capture_char_data(chunk):\n        captured_data.append(chunk)\n\n    p = expat.ParserCreate()\n    p.EntityDeclHandler = handle_entity_decl\n    p.CharacterDataHandler = capture_char_data\n\n    # Disable resolution of external parameter entities\n    p.SetParamEntityParsing(expat.XML_PARAM_ENTITY_PARSING_NEVER)\n\n    p.Parse(raw_xml_payload, 1)\n    return ''.join(captured_data), {}"}