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

Language: Python
Severity: High
CWE: CWE-611

## Source
7

## Flow
7-9

## Sink
9

## Vulnerable Code
```python
from lxml import etree
import boto3
from flask import request, jsonify

def process_iot_device_config(device_id):
    s3_client = boto3.client('s3')
    xml_payload = request.data
    try:
        parser = etree.XMLParser(resolve_entities=True)
        config_tree = etree.fromstring(xml_payload, parser)
        device_name = config_tree.find('.//device_name').text
        firmware_ver = config_tree.find('.//firmware').text
        telemetry_interval = config_tree.find('.//telemetry_rate').text
        config_obj = {
            'device_id': device_id,
            'name': device_name,
            'firmware': firmware_ver,
            'interval': telemetry_interval
        }
        s3_client.put_object(
            Bucket='iot-device-configs',
            Key=f'configs/{device_id}.json',
            Body=str(config_obj)
        )
        return jsonify({'status': 'configured', 'device': device_id})
    except Exception as err:
        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
```python
from lxml import etree
import boto3
from flask import request, jsonify

def process_iot_device_config(device_id):
    s3_client = boto3.client('s3')
    xml_payload = request.data
    try:
        parser = etree.XMLParser(
            resolve_entities=False,
            no_network=True,
            dtd_validation=False,
            load_dtd=False
        )
        config_tree = etree.fromstring(xml_payload, parser)
        device_name = config_tree.find('.//device_name').text
        firmware_ver = config_tree.find('.//firmware').text
        telemetry_interval = config_tree.find('.//telemetry_rate').text
        config_obj = {
            'device_id': device_id,
            'name': device_name,
            'firmware': firmware_ver,
            'interval': telemetry_interval
        }
        s3_client.put_object(
            Bucket='iot-device-configs',
            Key=f'configs/{device_id}.json',
            Body=str(config_obj)
        )
        return jsonify({'status': 'configured', 'device': device_id})
    except Exception as err:
        return jsonify({'error': str(err)}), 500
```
