{"title":"XML External Entity (XXE) Injection","language":"Python","severity":"High","cwe":"CWE-611","source_lines":[6],"flow_lines":[6,10],"sink_lines":[10],"vulnerable_code":"from xml.etree import ElementTree as ET\nfrom flask import Flask, request, jsonify\napp = Flask(__name__)\n@app.route('/iot/device/config', methods=['POST'])\ndef parse_device_telemetry():\n    raw_payload = request.data\n    if not raw_payload:\n        return jsonify({'err': 'empty payload'}), 400\n    try:\n        dev_tree = ET.fromstring(raw_payload)\n        cfg_node = dev_tree.find('.//deviceConfig')\n        fw_ver = cfg_node.get('firmware') if cfg_node is not None else 'unknown'\n        sensor_vals = {child.tag: child.text for child in dev_tree.iter('sensor')}\n        return jsonify({'firmware': fw_ver, 'sensors': sensor_vals})\n    except ET.ParseError as px:\n        return jsonify({'err': str(px)}), 422","explanation":"The raw POST body (request.data) is read directly from the HTTP request without any sanitization or validation, then passed to ET.fromstring() which parses it as XML. Python's xml.etree.ElementTree does not protect against XXE injection by default, and in older Python versions or when using alternative XML backends such as lxml, this allows XXE attacks where an attacker can craft a malicious XML payload with external entity declarations to read local files or perform SSRF. Even in modern CPython deployments, the absence of defusedxml represents a security misconfiguration and a violation of defensive coding best practices.","remediation":"The fix replaces the use of xml.etree.ElementTree.fromstring with defusedxml.ElementTree.fromstring, which is a drop-in replacement that automatically blocks XML External Entity (XXE) attacks, DTD processing, and entity expansion bombs. The ParseError import is also switched to defusedxml for consistency. This ensures that any malicious XML payload containing external entity declarations, DTD references, or billion-laughs attacks is rejected before parsing.","secure_code":"from defusedxml.ElementTree import fromstring, ParseError\nfrom flask import Flask, request, jsonify\napp = Flask(__name__)\n@app.route('/iot/device/config', methods=['POST'])\ndef parse_device_telemetry():\n    raw_payload = request.data\n    if not raw_payload:\n        return jsonify({'err': 'empty payload'}), 400\n    try:\n        dev_tree = fromstring(raw_payload)\n        cfg_node = dev_tree.find('.//deviceConfig')\n        fw_ver = cfg_node.get('firmware') if cfg_node is not None else 'unknown'\n        sensor_vals = {child.tag: child.text for child in dev_tree.iter('sensor')}\n        return jsonify({'firmware': fw_ver, 'sensors': sensor_vals})\n    except ParseError as px:\n        return jsonify({'err': str(px)}), 422"}