# Unsafe `yaml.full_load` Object Instantiation via Untrusted YAML Tag Resolution

Language: Python
Severity: Critical
CWE: CWE-502

## Source
8

## Flow
8-9

## Sink
9

## Vulnerable Code
```python
import yaml
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/iot/device/provision', methods=['POST'])
def provision_iot_device():
    device_manifest = request.data.decode('utf-8')
    device_config = yaml.full_load(device_manifest)
    device_id = device_config.get('device_id', 'unknown')
    firmware_version = device_config.get('firmware', '1.0.0')
    security_profile = device_config.get('security_profile', {})
    register_device_to_cloud(device_id, firmware_version, security_profile)
    return jsonify({'status': 'provisioned', 'device_id': device_id})

def register_device_to_cloud(dev_id, fw_ver, sec_prof):
    pass
```

## Explanation

The code uses `yaml.full_load()` to parse untrusted YAML data received from an HTTP POST request without validation. This allows attackers to instantiate arbitrary Python objects through YAML tags, leading to Remote Code Execution via deserialization attacks.

## Remediation

The fix replaces `yaml.full_load()` with `yaml.safe_load()`, which only allows basic Python types (strings, integers, lists, dicts, etc.) and does not resolve arbitrary Python object tags. Additionally, a type check ensures the parsed result is a dictionary before accessing its keys, preventing unexpected behavior from malformed input.

## Secure Code
```python
import yaml
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/iot/device/provision', methods=['POST'])
def provision_iot_device():
    device_manifest = request.data.decode('utf-8')
    device_config = yaml.safe_load(device_manifest)
    if not isinstance(device_config, dict):
        return jsonify({'status': 'error', 'message': 'Invalid manifest format'}), 400
    device_id = device_config.get('device_id', 'unknown')
    firmware_version = device_config.get('firmware', '1.0.0')
    security_profile = device_config.get('security_profile', {})
    register_device_to_cloud(device_id, firmware_version, security_profile)
    return jsonify({'status': 'provisioned', 'device_id': device_id})

def register_device_to_cloud(dev_id, fw_ver, sec_prof):
    pass
```
