{"title":"XML External Entity (XXE) via lxml.etree.parse on Untrusted XML","language":"Python","severity":"High","cwe":"CWE-611","source_lines":[7],"flow_lines":[7,9],"sink_lines":[9],"vulnerable_code":"from lxml import etree\nimport boto3\nfrom flask import request, jsonify\n\ndef process_iot_device_config(device_id):\n    s3_client = boto3.client('s3')\n    xml_payload = request.data\n    try:\n        parser = etree.XMLParser(resolve_entities=True)\n        config_tree = etree.fromstring(xml_payload, parser)\n        device_name = config_tree.find('.//device_name').text\n        firmware_ver = config_tree.find('.//firmware').text\n        telemetry_interval = config_tree.find('.//telemetry_rate').text\n        config_obj = {\n            'device_id': device_id,\n            'name': device_name,\n            'firmware': firmware_ver,\n            'interval': telemetry_interval\n        }\n        s3_client.put_object(\n            Bucket='iot-device-configs',\n            Key=f'configs/{device_id}.json',\n            Body=str(config_obj)\n        )\n        return jsonify({'status': 'configured', 'device': device_id})\n    except Exception as err:\n        return jsonify({'error': str(err)}), 500","explanation":"The code accepts untrusted XML data directly from the HTTP request body and parses it with resolve_entities=True enabled. This allows an attacker to inject malicious XXE payloads that can read local files, perform SSRF attacks, or cause denial of service through entity expansion attacks.","remediation":"The fix secures the XML parser by setting resolve_entities=False to prevent external entity expansion, no_network=True to block network-based entity fetching, and dtd_validation=False with load_dtd=False to prevent DTD processing entirely. These settings ensure that even if a malicious XML payload contains XXE declarations, they will not be resolved or processed.","secure_code":"from lxml import etree\nimport boto3\nfrom flask import request, jsonify\n\ndef process_iot_device_config(device_id):\n    s3_client = boto3.client('s3')\n    xml_payload = request.data\n    try:\n        parser = etree.XMLParser(\n            resolve_entities=False,\n            no_network=True,\n            dtd_validation=False,\n            load_dtd=False\n        )\n        config_tree = etree.fromstring(xml_payload, parser)\n        device_name = config_tree.find('.//device_name').text\n        firmware_ver = config_tree.find('.//firmware').text\n        telemetry_interval = config_tree.find('.//telemetry_rate').text\n        config_obj = {\n            'device_id': device_id,\n            'name': device_name,\n            'firmware': firmware_ver,\n            'interval': telemetry_interval\n        }\n        s3_client.put_object(\n            Bucket='iot-device-configs',\n            Key=f'configs/{device_id}.json',\n            Body=str(config_obj)\n        )\n        return jsonify({'status': 'configured', 'device': device_id})\n    except Exception as err:\n        return jsonify({'error': str(err)}), 500"}