# XML External Entity (XXE) Injection

Language: Python
Severity: High
CWE: CWE-611

## Source
9

## Flow
9-11

## Sink
11

## Vulnerable Code
```python
from xml.parsers import expat
def parse_iot_device_config(raw_xml_data):
    device_attrs = {}
    def handle_start(tag, attrs):
        device_attrs.update(attrs)
    def handle_char(data):
        device_attrs['_text'] = device_attrs.get('_text', '') + data
    p = expat.ParserCreate()
    p.StartElementHandler = handle_start
    p.CharacterDataHandler = handle_char
    p.Parse(raw_xml_data, 1)
    return device_attrs
```

## Explanation

The function `parse_iot_device_config` accepts raw XML data from an untrusted source (the function parameter `raw_xml_data`) and passes it directly to `expat.ParserCreate()` followed by `p.Parse()` without disabling external entity processing. The Python `expat` parser supports external entity references by default, allowing an attacker to inject a malicious DTD with an external entity declaration (e.g., `<!ENTITY xxe SYSTEM 'file:///etc/passwd'>`) which the parser will resolve, enabling arbitrary file read or SSRF.

## Remediation

The fix registers an `ExternalEntityRefHandler` that returns `False` to refuse resolution of any external entities, and an `EntityDeclHandler` that raises an exception if any entity declaration references an external system or public identifier. Together, these two handlers block both the resolution and declaration of external entities, fully mitigating the XXE attack vector while preserving the existing expat-based parsing logic.

## Secure Code
```python
from xml.parsers import expat

def parse_iot_device_config(raw_xml_data):
    device_attrs = {}

    def handle_start(tag, attrs):
        device_attrs.update(attrs)

    def handle_char(data):
        device_attrs['_text'] = device_attrs.get('_text', '') + data

    def handle_external_entity(context, base, system_id, public_id):
        # Refuse to resolve any external entities to prevent XXE
        return False

    def handle_entity_decl(entity_name, is_parameter_entity, value,
                           base, system_id, public_id, notation_name):
        # Reject any entity declarations that reference external resources
        if system_id is not None or public_id is not None:
            raise ValueError(
                "External entity declarations are not allowed in IoT device configurations"
            )

    p = expat.ParserCreate()
    p.StartElementHandler = handle_start
    p.CharacterDataHandler = handle_char
    # Disable external entity resolution to prevent XXE attacks
    p.ExternalEntityRefHandler = handle_external_entity
    p.EntityDeclHandler = handle_entity_decl
    p.Parse(raw_xml_data, 1)
    return device_attrs
```
