{"title":"Zip Slip via zipfile.extractall()","language":"Python","severity":"Critical","cwe":"CWE-22","source_lines":[8],"flow_lines":[8,10,11,13],"sink_lines":[13],"vulnerable_code":"import zipfile\nimport os\nfrom flask import request, jsonify\n\n@app.route('/api/iot/firmware/deploy', methods=['POST'])\ndef deploy_firmware_package():\n    device_id = request.form.get('device_id')\n    firmware_archive = request.files['firmware']\n    deployment_path = f'/opt/iot/devices/{device_id}/firmware'\n    os.makedirs(deployment_path, exist_ok=True)\n    archive_path = os.path.join('/tmp', firmware_archive.filename)\n    firmware_archive.save(archive_path)\n    with zipfile.ZipFile(archive_path, 'r') as fw_zip:\n        fw_zip.extractall(deployment_path)\n    return jsonify({'status': 'deployed', 'device': device_id, 'path': deployment_path})","explanation":"The code extracts a ZIP archive using zipfile.extractall() without validating the file paths within the archive. A malicious ZIP file can contain entries with path traversal sequences (e.g., ../../etc/passwd) that allow writing files outside the intended deployment_path directory, leading to arbitrary file write vulnerabilities (Zip Slip).","remediation":"The fix validates each file path within the ZIP archive before extraction by resolving the full real path and ensuring it stays within the intended deployment directory. Additionally, the device_id is validated against a whitelist pattern to prevent path traversal via the directory name itself, and the temporary archive file is cleaned up after use.","secure_code":"import zipfile\nimport os\nimport re\nfrom flask import request, jsonify\n\n@app.route('/api/iot/firmware/deploy', methods=['POST'])\ndef deploy_firmware_package():\n    device_id = request.form.get('device_id')\n    if not device_id or not re.match(r'^[a-zA-Z0-9_\\-]+$', device_id):\n        return jsonify({'status': 'error', 'message': 'Invalid device_id'}), 400\n    firmware_archive = request.files['firmware']\n    deployment_path = f'/opt/iot/devices/{device_id}/firmware'\n    os.makedirs(deployment_path, exist_ok=True)\n    archive_path = os.path.join('/tmp', firmware_archive.filename)\n    firmware_archive.save(archive_path)\n    with zipfile.ZipFile(archive_path, 'r') as fw_zip:\n        for member in fw_zip.namelist():\n            member_path = os.path.realpath(os.path.join(deployment_path, member))\n            if not member_path.startswith(os.path.realpath(deployment_path) + os.sep) and member_path != os.path.realpath(deployment_path):\n                os.remove(archive_path)\n                return jsonify({'status': 'error', 'message': f'Illegal path in archive: {member}'}), 400\n        fw_zip.extractall(deployment_path)\n    os.remove(archive_path)\n    return jsonify({'status': 'deployed', 'device': device_id, 'path': deployment_path})"}