# XML External Entity (XXE) via lxml.etree.fromstring()

Language: Python
Severity: High
CWE: CWE-611

## Source
1

## Flow
1-6

## Sink
6

## Vulnerable Code
```python
from lxml import etree
import boto3

def process_iot_device_telemetry(xml_payload):
    s3_client = boto3.client('s3')
    try:
        parsed_doc = etree.fromstring(xml_payload.encode())
        device_id = parsed_doc.find('.//deviceId').text
        sensor_data = parsed_doc.find('.//metrics').text
        timestamp = parsed_doc.find('.//timestamp').text
        s3_client.put_object(Bucket='iot-telemetry-logs', Key=f'{device_id}/{timestamp}.json', Body=sensor_data)
        return {'status': 'success', 'device': device_id, 'stored': True}
    except Exception as err:
        return {'status': 'error', 'message': str(err)}
```

## Explanation

The function accepts untrusted XML data via the xml_payload parameter and parses it directly using lxml.etree.fromstring() without disabling external entity resolution. This allows attackers to inject malicious XML containing external entity declarations that can read local files, perform SSRF attacks, or cause denial of service.

## Remediation

The fix creates a secure XMLParser instance with resolve_entities=False, no_network=True, and load_dtd=False, which prevents the parser from resolving external entities, accessing network resources, or loading external DTDs. This parser is then passed to etree.fromstring() to ensure all XML parsing uses the hardened configuration.

## Secure Code
```python
from lxml import etree
import boto3

def process_iot_device_telemetry(xml_payload):
    s3_client = boto3.client('s3')
    try:
        parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)
        parsed_doc = etree.fromstring(xml_payload.encode(), parser=parser)
        device_id = parsed_doc.find('.//deviceId').text
        sensor_data = parsed_doc.find('.//metrics').text
        timestamp = parsed_doc.find('.//timestamp').text
        s3_client.put_object(Bucket='iot-telemetry-logs', Key=f'{device_id}/{timestamp}.json', Body=sensor_data)
        return {'status': 'success', 'device': device_id, 'stored': True}
    except Exception as err:
        return {'status': 'error', 'message': str(err)}
```
