# Zip Slip via zipfile.extractall() on Untrusted Archives

Language: Python
Severity: Critical
CWE: CWE-22

## Source
8

## Flow
8-9-10-11

## Sink
11

## Vulnerable Code
```python
import zipfile
import os
from flask import request, jsonify

@app.route('/api/iot/firmware-update', methods=['POST'])
def deploy_firmware_package():
    device_id = request.form.get('device_id')
    firmware_archive = request.files['firmware']
    deployment_path = f'/opt/iot/devices/{device_id}/firmware'
    os.makedirs(deployment_path, exist_ok=True)
    archive_path = f'/tmp/{firmware_archive.filename}'
    firmware_archive.save(archive_path)
    with zipfile.ZipFile(archive_path, 'r') as fw_zip:
        fw_zip.extractall(deployment_path)
    return jsonify({'status': 'deployed', 'device': device_id})
```

## Explanation

The application accepts an uploaded firmware archive from an untrusted source without validating the file paths contained within the zip. When extractall() is called on line 14, malicious zip entries with path traversal sequences (e.g., '../../etc/cron.d/backdoor') can write files outside the intended deployment directory, leading to arbitrary file write and potential remote code execution.

## Remediation

The fix iterates over all entries in the zip archive before extraction and resolves each member's target path using os.path.realpath(). It then verifies that every resolved path starts with the intended deployment directory, rejecting the archive entirely if any entry attempts path traversal. Additionally, the temporary archive file is cleaned up after extraction.

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

@app.route('/api/iot/firmware-update', methods=['POST'])
def deploy_firmware_package():
    device_id = request.form.get('device_id')
    firmware_archive = request.files['firmware']
    deployment_path = os.path.realpath(f'/opt/iot/devices/{device_id}/firmware')
    os.makedirs(deployment_path, exist_ok=True)
    archive_path = f'/tmp/{firmware_archive.filename}'
    firmware_archive.save(archive_path)
    with zipfile.ZipFile(archive_path, 'r') as fw_zip:
        for member in fw_zip.infolist():
            member_path = os.path.realpath(os.path.join(deployment_path, member.filename))
            if not member_path.startswith(deployment_path + os.sep) and member_path != deployment_path:
                return jsonify({'status': 'error', 'message': f'Malicious path detected in archive: {member.filename}'}), 400
        fw_zip.extractall(deployment_path)
    os.remove(archive_path)
    return jsonify({'status': 'deployed', 'device': device_id})
```
