# XML External Entity (XXE) Injection

Language: Python
Severity: Critical
CWE: CWE-611

## Source
6

## Flow
6-9-10-11-12

## Sink
10

## Vulnerable Code
```python
from lxml import etree
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/iot/device/config', methods=['POST'])
def parse_device_telemetry():
    raw_payload = request.data
    if not raw_payload:
        return jsonify({'err': 'empty payload'}), 400
    parser = etree.XMLParser(resolve_entities=True, no_network=False)
    dev_tree = etree.fromstring(raw_payload, parser)
    sensor_id = dev_tree.findtext('.//sensorId')
    readings = dev_tree.findtext('.//readings')
    return jsonify({'sensor': sensor_id, 'data': readings}), 200
```

## Explanation

The application reads raw XML data directly from the HTTP request body (line 6) and parses it using an lxml XMLParser configured with resolve_entities=True and no_network=False (line 9). This allows an attacker to embed external entity declarations (XXE) in the XML payload, enabling arbitrary file reads from the server filesystem or SSRF against internal network endpoints. The tainted input flows directly into etree.fromstring() without any sanitization or safe parser configuration.

## Remediation

The fix configures the lxml XMLParser with resolve_entities=False, no_network=True, dtd_validation=False, and load_dtd=False, which collectively prevent the parser from resolving external (and internal) entity references and from loading any external DTDs or making network requests. Additionally, XML parsing is wrapped in a try/except block to gracefully handle malformed or malicious XML payloads that might cause parsing errors.

## Secure Code
```python
from lxml import etree
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/iot/device/config', methods=['POST'])
def parse_device_telemetry():
    raw_payload = request.data
    if not raw_payload:
        return jsonify({'err': 'empty payload'}), 400
    parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)
    try:
        dev_tree = etree.fromstring(raw_payload, parser)
    except etree.XMLSyntaxError:
        return jsonify({'err': 'invalid XML payload'}), 400
    sensor_id = dev_tree.findtext('.//sensorId')
    readings = dev_tree.findtext('.//readings')
    return jsonify({'sensor': sensor_id, 'data': readings}), 200
```
