# Unsafe YAML Deserialization via yaml.load() with FullLoader

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.load(device_manifest, Loader=yaml.FullLoader)
    device_id = device_config.get('device_id', 'unknown')
    firmware_ver = device_config.get('firmware_version', '1.0.0')
    capabilities = device_config.get('capabilities', [])
    register_device_to_cloud(device_id, firmware_ver, capabilities)
    return jsonify({'status': 'provisioned', 'device_id': device_id})

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

## Explanation

The application accepts untrusted YAML data from HTTP POST requests and deserializes it using yaml.load() with FullLoader. This allows attackers to execute arbitrary Python code through YAML's ability to instantiate arbitrary Python objects, leading to Remote Code Execution (RCE).

## Remediation

The fix replaces yaml.load() with yaml.safe_load(), which only deserializes standard YAML tags (strings, numbers, lists, dicts) and refuses to instantiate arbitrary Python objects. Additionally, a type check ensures the deserialized result is a dictionary before accessing its fields, 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_ver = device_config.get('firmware_version', '1.0.0')
    capabilities = device_config.get('capabilities', [])
    register_device_to_cloud(device_id, firmware_ver, capabilities)
    return jsonify({'status': 'provisioned', 'device_id': device_id})

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