# XML External Entity (XXE) via lxml resolve_entities=True

Language: Python
Severity: Critical
CWE: CWE-611

## Source
6

## Flow
6-9

## Sink
9

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

def process_cloud_deployment_manifest(manifest_xml):
    s3_client = boto3.client('s3')
    parser = etree.XMLParser(resolve_entities=True, no_network=False)
    try:
        deployment_doc = etree.fromstring(manifest_xml.encode(), parser)
        service_name = deployment_doc.find('.//ServiceName').text
        region = deployment_doc.find('.//Region').text
        instance_type = deployment_doc.find('.//InstanceType').text
        config_data = {
            'service': service_name,
            'region': region,
            'instance': instance_type,
            'status': 'parsed'
        }
        bucket_key = f"deployments/{service_name}/config.json"
        return jsonify({'success': True, 'config': config_data, 's3_path': bucket_key})
    except Exception as e:
        return jsonify({'error': str(e)}), 400
```

## Explanation

The code creates an XML parser with resolve_entities=True and no_network=False, allowing external entity resolution. When untrusted XML from the manifest_xml parameter is parsed using etree.fromstring() with this unsafe parser, attackers can include external entity references to read local files, perform SSRF attacks, or cause denial of service.

## Remediation

The fix secures the XML parser by setting resolve_entities=False to prevent external entity resolution, no_network=True to block network access during parsing, and dtd_validation=False and load_dtd=False to prevent DTD loading entirely. These settings ensure that malicious XML payloads containing external entity references cannot be exploited to read files, perform SSRF, or cause denial of service.

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

def process_cloud_deployment_manifest(manifest_xml):
    s3_client = boto3.client('s3')
    parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)
    try:
        deployment_doc = etree.fromstring(manifest_xml.encode(), parser)
        service_name = deployment_doc.find('.//ServiceName').text
        region = deployment_doc.find('.//Region').text
        instance_type = deployment_doc.find('.//InstanceType').text
        config_data = {
            'service': service_name,
            'region': region,
            'instance': instance_type,
            'status': 'parsed'
        }
        bucket_key = f"deployments/{service_name}/config.json"
        return jsonify({'success': True, 'config': config_data, 's3_path': bucket_key})
    except Exception as e:
        return jsonify({'error': str(e)}), 400
```
