# YAML Deserialization via yaml.load()

Language: Python
Severity: Critical
CWE: CWE-502

## Source
6

## Flow
6-7

## Sink
7

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

def apply_iot_device_config():
    device_id = request.args.get('device_id')
    config_payload = request.data.decode('utf-8')
    device_settings = yaml.load(config_payload)
    device_settings['device_id'] = device_id
    device_settings['status'] = 'configured'
    with open(f'/var/iot/devices/{device_id}.conf', 'w') as conf_file:
        conf_file.write(str(device_settings))
    return jsonify({'message': 'Device configured successfully', 'device': device_id})
```

## Explanation

The code uses yaml.load() without a Loader argument, which defaults to the unsafe FullLoader in older PyYAML versions or requires explicit specification in newer versions. This allows arbitrary Python object instantiation through specially crafted YAML payloads. An attacker can send malicious YAML that executes arbitrary code when deserialized.

## Remediation

The fix replaces yaml.load() with yaml.safe_load(), which only allows basic Python types (strings, numbers, lists, dicts) and prevents arbitrary object instantiation. Additionally, input validation was added for device_id to prevent path traversal attacks, and the parsed YAML is verified to be a dictionary before processing.

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

def apply_iot_device_config():
    device_id = request.args.get('device_id')
    if not device_id or not re.match(r'^[a-zA-Z0-9_\-]+$', device_id):
        return jsonify({'error': 'Invalid device_id'}), 400
    config_payload = request.data.decode('utf-8')
    device_settings = yaml.safe_load(config_payload)
    if not isinstance(device_settings, dict):
        return jsonify({'error': 'Invalid configuration format'}), 400
    device_settings['device_id'] = device_id
    device_settings['status'] = 'configured'
    conf_path = os.path.join('/var/iot/devices', f'{device_id}.conf')
    with open(conf_path, 'w') as conf_file:
        conf_file.write(str(device_settings))
    return jsonify({'message': 'Device configured successfully', 'device': device_id})
```
