{"title":"Zip Slip via Unsafe Archive Extraction Path Traversal","language":"Python","severity":"Critical","cwe":"CWE-22","source_lines":[8],"flow_lines":[8,9,10,11],"sink_lines":[9,11],"vulnerable_code":"import zipfile\nimport os\n\ndef deploy_iot_firmware_package(firmware_zip, device_id):\n    deployment_path = f\"/opt/iot/devices/{device_id}/firmware\"\n    os.makedirs(deployment_path, exist_ok=True)\n    with zipfile.ZipFile(firmware_zip, 'r') as archive:\n        for component in archive.namelist():\n            target_location = os.path.join(deployment_path, component)\n            with open(target_location, 'wb') as output:\n                output.write(archive.read(component))\n    return {\"status\": \"deployed\", \"device\": device_id, \"path\": deployment_path}","explanation":"The code extracts zip archive members without validating their paths for directory traversal sequences. An attacker can include filenames with '../' sequences in the zip archive to write files outside the intended deployment directory, potentially overwriting critical system files.","remediation":"The fix resolves the full real path of each target file using os.path.realpath() and then validates that it starts with the intended deployment directory prefix. If any archive member attempts to traverse outside the deployment directory via '../' sequences, the function raises a ValueError, preventing the file from being written to an unintended location.","secure_code":"import zipfile\nimport os\n\ndef deploy_iot_firmware_package(firmware_zip, device_id):\n    deployment_path = f\"/opt/iot/devices/{device_id}/firmware\"\n    os.makedirs(deployment_path, exist_ok=True)\n    with zipfile.ZipFile(firmware_zip, 'r') as archive:\n        for component in archive.namelist():\n            # Resolve the target path and ensure it stays within the deployment directory\n            target_location = os.path.realpath(os.path.join(deployment_path, component))\n            safe_deployment_path = os.path.realpath(deployment_path)\n            if not target_location.startswith(safe_deployment_path + os.sep) and target_location != safe_deployment_path:\n                raise ValueError(f\"Unsafe path detected in archive: {component}\")\n            # Create parent directories if needed for nested components\n            os.makedirs(os.path.dirname(target_location), exist_ok=True)\n            # Skip directory entries\n            if component.endswith('/'):\n                continue\n            with open(target_location, 'wb') as output:\n                output.write(archive.read(component))\n    return {\"status\": \"deployed\", \"device\": device_id, \"path\": deployment_path}"}