{"title":"XML External Entity (XXE) Injection via lxml.etree.fromstring()","language":"Python","severity":"High","cwe":"CWE-611","source_lines":[4],"flow_lines":[4,7],"sink_lines":[7],"vulnerable_code":"from lxml import etree\nimport boto3\n\ndef process_iot_device_telemetry(xml_payload):\n    s3_client = boto3.client('s3')\n    parser = etree.XMLParser(resolve_entities=True)\n    telemetry_doc = etree.fromstring(xml_payload.encode(), parser)\n    device_id = telemetry_doc.find('.//deviceId').text\n    sensor_data = telemetry_doc.find('.//sensorReadings').text\n    metadata = {'device': device_id, 'timestamp': telemetry_doc.get('timestamp')}\n    s3_client.put_object(Bucket='iot-telemetry-bucket', Key=f'devices/{device_id}/data.xml', Body=sensor_data, Metadata=metadata)\n    return {'status': 'processed', 'device': device_id, 'data_size': len(sensor_data)}","explanation":"The function accepts untrusted XML input (xml_payload) and parses it with lxml.etree using an XMLParser configured with resolve_entities=True. This allows attackers to inject XML External Entity (XXE) references that can lead to local file disclosure, SSRF attacks, or denial of service.","remediation":"The fix secures the XMLParser by setting resolve_entities=False to prevent entity expansion, no_network=True to block network-based entity resolution, and dtd_validation=False with load_dtd=False to prevent DTD loading entirely. These settings ensure that any XXE payloads embedded in the XML input will not be processed or resolved.","secure_code":"from lxml import etree\nimport boto3\n\ndef process_iot_device_telemetry(xml_payload):\n    s3_client = boto3.client('s3')\n    parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)\n    telemetry_doc = etree.fromstring(xml_payload.encode(), parser)\n    device_id = telemetry_doc.find('.//deviceId').text\n    sensor_data = telemetry_doc.find('.//sensorReadings').text\n    metadata = {'device': device_id, 'timestamp': telemetry_doc.get('timestamp')}\n    s3_client.put_object(Bucket='iot-telemetry-bucket', Key=f'devices/{device_id}/data.xml', Body=sensor_data, Metadata=metadata)\n    return {'status': 'processed', 'device': device_id, 'data_size': len(sensor_data)}"}