# ZIP Validations

## Why Security Checks Are Needed Before Processing ZIP Files

Processing untrusted ZIP archives without validation can lead to:

- **Zip bombs** (excessive compression ratios causing resource exhaustion)
- **Path traversal attacks** (`../` paths escaping extraction directory)
- **Denial of service** (massive member counts, total extracted sizes, or individual file sizes)
- **Encrypted archives** (bypassing security scanning)
- **Unsupported compression methods** (causing application crashes)
- **Unicode normalization attacks** (duplicate filenames exploiting filesystem behaviors)
- **Special file exploitation** (symlinks, devices, FIFOs)

## Capabilities

The `validate_zip` function performs comprehensive security checks:

| Check | Description |
|-------|-------------|
| **Compressed Size** | Enforces maximum archive file size (bytes) |
| **Member Count** | Limits number of entries in the archive |
| **Total Extracted Size** | Prevents zip bombs with inflated total decompressed size |
| **Individual File Size** | Caps size per extracted file |
| **Decompression Ratio** | Rejects files with excessive compression ratios (e.g., >100x) |
| **Path Traversal** | Rejects absolute paths, `..`, `.`, backslashes, and drive letters |
| **Filename Length** | Limits encoded path length (UTF-8 bytes) |
| **Directory Depth** | Prevents excessive directory nesting |
| **Duplicate Detection** | Identifies duplicate filenames after Unicode normalization |
| **NUL Byte Rejection** | Blocks filenames containing NUL characters |
| **Symlink/Device Rejection** | Blocks symbolic links and special device files |
| **Encryption Detection** | Rejects encrypted ZIP entries |
| **Compression Validation** | Rejects unsupported compression methods |
| **Remote File Restriction** | Strictly restricts remote access to HTTPS only |


## Usage Options

### Parameters

| Parameter                  | Description                                      |
|----------------------------|--------------------------------------------------|
| `func_or_path`             | Path or callable for the zip archive.            |
| `max_file_size`            | Max size of the archive file.                    |
| `max_uncompressed_ratio`   | Max compression ratio.                           |
| `max_zip_members`          | Max number of members in the archive.            |
| `max_total_extracted_size` | Max total size of extracted content.             |
| `max_individual_file_size` | Max size of any single file.                     |
| `max_filename_length`      | Max length of filenames.                         |
| `max_directory_depth`      | Max directory nesting depth.                     |

### Defaults 

```bash
DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024          # 100 MiB
DEFAULT_MAX_UNCOMPRESSED_RATIO = 100               # 100:1
DEFAULT_MAX_ZIP_MEMBERS = 10_000
DEFAULT_MAX_TOTAL_EXTRACTED_SIZE = 1 * 1024 * 1024**3  # 1 GiB
DEFAULT_MAX_INDIVIDUAL_FILE_SIZE = 100 * 1024 * 1024   # 100 MiB
DEFAULT_MAX_FILENAME_LENGTH = 255
DEFAULT_MAX_DIRECTORY_DEPTH = 20
```

And note:
```
HEAD_TIMEOUT = 10
DOWNLOAD_TIMEOUT = 30
```

## How the Checks Can Be Used

The function operates in three modes:

### 1. Direct Call / CLI Mode

```python
# Validate a local ZIP file
result = validate_zip("path/to/archive.zip", max_zip_members=1000)

# Validate remote file (HTTPS only)
result = validate_zip("https://example.com/archive.zip", max_file_size=100*1024*1024)

# Returns True if valid, False if invalid (errors printed to stdout)
```

### 2. Decorator Mode

```python
# Bare decorator (uses default limits)
@validate_zip
def extract_data(archive_path):
    # ZIP already validated before function body runs
    return process(archive_path)

# Decorator with custom limits
@validate_zip(
    max_file_size=500*1024*1024,
    max_zip_members=5000,
    max_uncompressed_ratio=50
)
def extract_secure(file_input):
    return safe_extract(file_input)

# Explicit argument name targeting
@validate_zip("zip_path", max_individual_file_size=10*1024*1024)
def process_file(zip_path):
    return handle_archive(zip_path)
```

### 3. Factory Mode (Preconfigured Validator)

```python
# Create a reusable validator with custom defaults
zip_validator = validate_zip(
    max_total_extracted_size=1*1024*1024*1024,
    max_zip_members=100,
    max_directory_depth=10
)

@zip_validator
def process_small_archive(archive_path):
    # Uses preconfigured limits
    return handle_small(archive_path)
```


### Return Values

| Mode | Returns |
|------|---------|
| **Direct Call** | `True` if valid, `False` if invalid (errors printed to stdout) |
| **Decorator** | Wrapped function (raises `ZipValidationError` on failure) |


### Exceptions

- **`ZipValidationError`**: Raised when validation fails (decorator mode only)
- **`zipfile.BadZipFile`**: Propagated from invalid or corrupt ZIP files
- **`FileNotFoundError`**, **`PermissionError`**: Propagated from file operations
- **`urllib.error.URLError`**, **`urllib.error.HTTPError`**: Propagated from remote operations