{"title":"YAML Deserialization Remote Code Execution","language":"Python","severity":"Critical","cwe":"CWE-502","source_lines":[6],"flow_lines":[6,7],"sink_lines":[7],"vulnerable_code":"import yaml\nfrom flask import Flask, request, jsonify\napp = Flask(__name__)\n@app.route('/api/iot/device/config', methods=['POST'])\ndef sync_device_cfg():\n    raw_payload = request.data.decode('utf-8')\n    try:\n        dev_cfg = yaml.load(raw_payload)\n        device_id = dev_cfg.get('device_id', 'unknown')\n        thresholds = dev_cfg.get('thresholds', {})\n        return jsonify({'status': 'synced', 'device': device_id, 'cfg': thresholds})\n    except Exception as exc:\n        return jsonify({'error': str(exc)}), 400","explanation":"The raw HTTP request body is decoded on line 6 and passed directly to yaml.load() on line 7 without specifying a safe Loader argument. yaml.load() with no Loader (or with the default FullLoader/UnsafeLoader) can deserialize arbitrary Python objects embedded in the YAML payload, enabling remote code execution. An attacker can craft a malicious YAML payload using Python-specific tags to execute OS commands on the server.","remediation":"The fix replaces `yaml.load(raw_payload)` with `yaml.safe_load(raw_payload)`, which uses the SafeLoader that only deserializes standard YAML types (strings, numbers, lists, dicts) and refuses to instantiate arbitrary Python objects. An additional type check ensures the parsed result is a dictionary, preventing unexpected input shapes from propagating further.","secure_code":"import yaml\nfrom flask import Flask, request, jsonify\napp = Flask(__name__)\n@app.route('/api/iot/device/config', methods=['POST'])\ndef sync_device_cfg():\n    raw_payload = request.data.decode('utf-8')\n    try:\n        dev_cfg = yaml.safe_load(raw_payload)\n        if not isinstance(dev_cfg, dict):\n            return jsonify({'error': 'Invalid configuration format: expected a YAML mapping'}), 400\n        device_id = dev_cfg.get('device_id', 'unknown')\n        thresholds = dev_cfg.get('thresholds', {})\n        return jsonify({'status': 'synced', 'device': device_id, 'cfg': thresholds})\n    except Exception as exc:\n        return jsonify({'error': str(exc)}), 400"}