{"title":"XML External Entity (XXE) Injection","language":"Python","severity":"High","cwe":"CWE-611","source_lines":[6],"flow_lines":[6,7,8,9],"sink_lines":[7],"vulnerable_code":"from xml.etree import ElementTree as ET\nimport flask\napp = flask.Flask(__name__)\n@app.route('/iot/device/config', methods=['POST'])\ndef parse_device_config():\n    raw_payload = flask.request.data\n    try:\n        cfg_tree = ET.fromstring(raw_payload)\n        dev_id = cfg_tree.findtext('deviceId')\n        fw_ver = cfg_tree.findtext('firmwareVersion')\n        return flask.jsonify({'device': dev_id, 'firmware': fw_ver})\n    except ET.ParseError as pe:\n        return flask.jsonify({'err': str(pe)}), 400","explanation":"The raw HTTP POST body (raw_payload) is read directly from the untrusted network request on line 6 and immediately passed to ET.fromstring() on line 7 without any sanitization or XXE-hardening. Python's xml.etree.ElementTree does not fully protect against all XXE attacks, and combined with no use of defusedxml or explicit feature-flag hardening, a crafted DOCTYPE with external entity references can be injected to exfiltrate local files (e.g., /etc/passwd) or internal metadata endpoints.","remediation":"The fix replaces the import of the standard library's xml.etree.ElementTree with defusedxml.ElementTree, which is a drop-in replacement that disables external entity resolution, DTD processing, and entity expansion by default. This prevents any XXE payloads embedded in crafted DOCTYPE declarations from being processed, blocking file exfiltration and SSRF attacks while maintaining identical API behavior for legitimate XML payloads.","secure_code":"import defusedxml.ElementTree as ET\nimport flask\napp = flask.Flask(__name__)\n@app.route('/iot/device/config', methods=['POST'])\ndef parse_device_config():\n    raw_payload = flask.request.data\n    try:\n        cfg_tree = ET.fromstring(raw_payload)\n        dev_id = cfg_tree.findtext('deviceId')\n        fw_ver = cfg_tree.findtext('firmwareVersion')\n        return flask.jsonify({'device': dev_id, 'firmware': fw_ver})\n    except ET.ParseError as pe:\n        return flask.jsonify({'err': str(pe)}), 400"}