{"title":"Zip Slip via zipfile.extractall() on Untrusted Archives","language":"Python","severity":"Critical","cwe":"CWE-22","source_lines":[2],"flow_lines":[2,6,7],"sink_lines":[7],"vulnerable_code":"def deploy_iot_firmware_package(device_id, firmware_archive):\n    import zipfile\n    import os\n    deployment_path = f\"/opt/iot/devices/{device_id}/firmware\"\n    os.makedirs(deployment_path, exist_ok=True)\n    with zipfile.ZipFile(firmware_archive, 'r') as fw_zip:\n        fw_zip.extractall(deployment_path)\n    config_file = os.path.join(deployment_path, \"device.conf\")\n    with open(config_file, 'r') as cfg:\n        return cfg.read()","explanation":"The function accepts an untrusted firmware_archive parameter and extracts it without validating file paths within the archive. A malicious ZIP file can contain entries with path traversal sequences (e.g., '../../../etc/cron.d/malicious') that allow writing files outside the intended deployment_path directory, potentially overwriting critical system files.","remediation":"The fix replaces the unsafe extractall() call with a two-pass approach: first validating all archive members to ensure none resolve outside the deployment directory, then extracting each member individually after re-verifying the path. Each member's resolved path is checked against the canonical deployment_path using os.path.realpath() to prevent path traversal via '../' sequences or symlinks.","secure_code":"def deploy_iot_firmware_package(device_id, firmware_archive):\n    import zipfile\n    import os\n    deployment_path = f\"/opt/iot/devices/{device_id}/firmware\"\n    os.makedirs(deployment_path, exist_ok=True)\n    with zipfile.ZipFile(firmware_archive, 'r') as fw_zip:\n        for member in fw_zip.infolist():\n            member_path = os.path.realpath(os.path.join(deployment_path, member.filename))\n            if not member_path.startswith(os.path.realpath(deployment_path) + os.sep) and member_path != os.path.realpath(deployment_path):\n                raise ValueError(f\"Attempted path traversal in firmware archive: {member.filename}\")\n        for member in fw_zip.infolist():\n            member_path = os.path.realpath(os.path.join(deployment_path, member.filename))\n            if not member_path.startswith(os.path.realpath(deployment_path) + os.sep) and member_path != os.path.realpath(deployment_path):\n                raise ValueError(f\"Attempted path traversal in firmware archive: {member.filename}\")\n            fw_zip.extract(member, deployment_path)\n    config_file = os.path.join(deployment_path, \"device.conf\")\n    with open(config_file, 'r') as cfg:\n        return cfg.read()"}