# Unsafe YAML Deserialization via yaml.load()

Language: Python
Severity: Critical
CWE: CWE-502

## Source
10

## Flow
10-11

## Sink
11

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

app = Flask(__name__)

@app.route('/iot/device/configure', methods=['POST'])
def update_device_config():
    device_id = request.headers.get('X-Device-ID')
    config_payload = request.data.decode('utf-8')
    device_settings = yaml.load(config_payload, Loader=yaml.Loader)
    device_settings['device_id'] = device_id
    device_settings['last_updated'] = __import__('time').time()
    apply_device_configuration(device_settings)
    return jsonify({'status': 'configured', 'device': device_id})

def apply_device_configuration(settings):
    pass
```

## Explanation

The application uses yaml.load() with yaml.Loader on untrusted user input from the HTTP POST request body without validation. This allows arbitrary Python object instantiation, enabling remote code execution through malicious YAML payloads containing Python object serialization directives.

## Remediation

The fix replaces yaml.load() with yaml.safe_load(), which only deserializes standard YAML data types (strings, numbers, lists, dicts) and refuses to instantiate arbitrary Python objects. Additionally, error handling is added to gracefully reject malformed YAML, and a type check ensures the parsed result is a dictionary as expected for device configuration.

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

app = Flask(__name__)

@app.route('/iot/device/configure', methods=['POST'])
def update_device_config():
    device_id = request.headers.get('X-Device-ID')
    config_payload = request.data.decode('utf-8')
    try:
        device_settings = yaml.safe_load(config_payload)
    except yaml.YAMLError as e:
        return jsonify({'status': 'error', 'message': 'Invalid YAML payload'}), 400
    if not isinstance(device_settings, dict):
        return jsonify({'status': 'error', 'message': 'Configuration must be a YAML mapping'}), 400
    device_settings['device_id'] = device_id
    device_settings['last_updated'] = __import__('time').time()
    apply_device_configuration(device_settings)
    return jsonify({'status': 'configured', 'device': device_id})

def apply_device_configuration(settings):
    pass
```
