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

Language: Python
Severity: High
CWE: CWE-611

## Source
4

## Flow
4-6

## Sink
6

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

def sync_iot_device_config(device_xml_stream, device_id):
    s3_client = boto3.client('s3')
    config_tree = etree.parse(device_xml_stream)
    device_name = config_tree.find('.//device_name').text
    firmware_ver = config_tree.find('.//firmware').text
    telemetry_interval = config_tree.find('.//telemetry_interval').text
    config_data = {'device': device_name, 'firmware': firmware_ver, 'interval': telemetry_interval}
    s3_client.put_object(Bucket='iot-device-configs', Key=f'{device_id}.json', Body=str(config_data))
    return {'status': 'synced', 'device': device_name, 'firmware': firmware_ver}
```

## Explanation

The function uses lxml.etree.parse() to parse XML from an untrusted device_xml_stream without disabling external entity resolution. This allows an attacker to inject malicious XML with external entity references that can read local files, perform SSRF attacks, or cause denial of service through billion laughs attacks.

## Remediation

The fix creates a secure XMLParser instance with resolve_entities=False, no_network=True, dtd_validation=False, and load_dtd=False, which prevents the parser from resolving external entities, accessing network resources, or loading DTDs. This hardened parser is then passed to etree.parse() to safely parse the XML input without XXE vulnerability.

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

def sync_iot_device_config(device_xml_stream, device_id):
    s3_client = boto3.client('s3')
    parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)
    config_tree = etree.parse(device_xml_stream, parser)
    device_name = config_tree.find('.//device_name').text
    firmware_ver = config_tree.find('.//firmware').text
    telemetry_interval = config_tree.find('.//telemetry_interval').text
    config_data = {'device': device_name, 'firmware': firmware_ver, 'interval': telemetry_interval}
    s3_client.put_object(Bucket='iot-device-configs', Key=f'{device_id}.json', Body=str(config_data))
    return {'status': 'synced', 'device': device_name, 'firmware': firmware_ver}
```
