# XML External Entity (XXE) via lxml.etree.fromstring with entity resolution

Language: Python
Severity: Critical
CWE: CWE-611

## Source
6

## Flow
6-7

## Sink
7

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

def process_cloud_config_manifest(manifest_xml, bucket_name):
    s3_client = boto3.client('s3')
    parser = etree.XMLParser(resolve_entities=True, no_network=False)
    config_tree = etree.fromstring(manifest_xml.encode(), parser)
    deployment_region = config_tree.find('.//Region').text
    instance_type = config_tree.find('.//InstanceType').text
    security_groups = [sg.text for sg in config_tree.findall('.//SecurityGroup')]
    metadata = {'region': deployment_region, 'type': instance_type, 'sg': security_groups}
    s3_client.put_object(Bucket=bucket_name, Key=f'deployments/{deployment_region}/config.json', Body=str(metadata))
    return {'status': 'deployed', 'region': deployment_region, 'metadata': metadata}
```

## Explanation

The code accepts untrusted XML input (manifest_xml) and parses it with lxml.etree using resolve_entities=True and no_network=False, enabling XXE attacks. An attacker can inject malicious external entity declarations to 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 entity expansion, no_network=True to block network-based entity resolution, and explicitly disabling DTD loading and validation. These settings prevent attackers from injecting external entity declarations that could read local files, perform SSRF, or cause denial of service.

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

def process_cloud_config_manifest(manifest_xml, bucket_name):
    s3_client = boto3.client('s3')
    parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)
    config_tree = etree.fromstring(manifest_xml.encode(), parser)
    deployment_region = config_tree.find('.//Region').text
    instance_type = config_tree.find('.//InstanceType').text
    security_groups = [sg.text for sg in config_tree.findall('.//SecurityGroup')]
    metadata = {'region': deployment_region, 'type': instance_type, 'sg': security_groups}
    s3_client.put_object(Bucket=bucket_name, Key=f'deployments/{deployment_region}/config.json', Body=str(metadata))
    return {'status': 'deployed', 'region': deployment_region, 'metadata': metadata}
```
