[Security] Zip/Rar/7z Archive Extraction Path Traversal (Incomplete CVE-2007-4559 Mitigation)

## Security Vulnerability Report

**Reporter:** Conner Webber (conner.webber000@gmail.com)
**Severity:** HIGH (CVSS 3.1: 8.2)
**CWE:** CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)

## Summary

The `ZipExtractor`, `RarExtractor`, and `SevenZipExtractor` classes in `datasets/utils/extract.py` call `extractall()` without any path traversal validation on archive member filenames. This allows a malicious archive containing entries with directory traversal sequences (e.g., `../../../.bashrc`) to write files outside the intended output directory.

Notably, the adjacent `TarExtractor` class in the same file **already implements** a `safemembers()` method (lines 87-120) that explicitly mitigates CVE-2007-4559 for tar archives by filtering out members with absolute paths or `..` components. The zip, rar, and 7zip extractors were never given the same treatment.

## Vulnerability Details

### HIGH: ZipExtractor extractall() Without Path Traversal Validation

- **File:** `datasets/utils/extract.py:188`
- `ZipExtractor.extract()` calls `zipfile.ZipFile.extractall()` with zero path validation on member filenames
- A malicious zip file in a dataset with entries like `../../../.bashrc` writes outside the output directory
- Exploitable when loading any dataset containing crafted zip archives via `datasets.load_dataset()`

### MEDIUM: RarExtractor and SevenZipExtractor Same Issue

- **File:** `extract.py:206-214` (Rar), `extract.py:251-252` (7zip)
- Both call `extractall()` without member path validation
- Same fix pattern needed as zip

## Steps to Reproduce

1. Create a malicious zip file with a path traversal entry:

```python
import zipfile

with zipfile.ZipFile('malicious.zip', 'w') as zf:
    zf.writestr('../../../tmp/pwned.txt', 'arbitrary file write')
```

2. Host as a dataset or place in a dataset's archive files
3. Load the dataset with `datasets.load_dataset()` — the zip extractor writes `pwned.txt` to `/tmp/` (or any traversed path) instead of the output directory

## Impact

Arbitrary file write on the filesystem of any user who loads a dataset containing a crafted zip/rar/7z archive. This could lead to:

- **Overwriting configuration files** (`.bashrc`, `.profile`, SSH keys)
- **Code execution** via cron jobs, shell profiles, or Python path injection
- **Denial of service** via overwriting critical files

This is especially dangerous because `datasets` is widely used (50M+ monthly downloads) and users routinely load untrusted community datasets from the Hugging Face Hub.

## Suggested Fix

Apply the same `safemembers()` pattern already used for `TarExtractor`. For zip:

```python
def _safemembers(zip_file, output_path):
    safe = []
    for member in zip_file.infolist():
        member_path = os.path.realpath(os.path.join(output_path, member.filename))
        if not member_path.startswith(os.path.realpath(output_path) + os.sep):
            logger.warning(f"Skipping {member.filename} — path traversal detected")
            continue
        safe.append(member)
    return safe
```

Then extract only safe members instead of calling `extractall()`.

## Affected Versions

All current versions of `datasets` (pip package) are believed to be affected.

## References

- [CVE-2007-4559](https://nvd.nist.gov/vuln/detail/CVE-2007-4559) — The original tar path traversal vulnerability that `TarExtractor.safemembers()` was built to mitigate
- [CWE-22](https://cwe.mitre.org/data/definitions/22.html) — Path Traversal
- `datasets/utils/extract.py` lines 87-120 (existing tar mitigation) vs lines 188, 206-214, 251-252 (missing zip/rar/7z mitigation)

Comments:
Hi! I'm currently exploring the codebase and came across this issue. I'd be interested in investigating it further and trying to reproduce the behavior locally.

If no one is actively working on it, I'd be happy to take a look and attempt a fix.


The steps to reproduce don't cause any file extraction outside of output_path

see https://docs.python.org/2/library/zipfile.html#zipfile.ZipFile.extract

> Note If a member filename is an absolute path, a drive/UNC sharepoint and leading (back)slashes will be stripped, e.g.: ///foo/bar becomes foo/bar on Unix, and C:\foo\bar becomes foo\bar on Windows. And all ".." components in a member filename will be removed, e.g.: ../../foo../../ba..r becomes foo../ba..r. On Windows illegal characters (:, <, >, |, ", ?, and *) replaced by underscore (_).

I opened a PR with safeguards anyways

Thanks @lhoestq — you're right, I missed that \ already strips \ components and absolute paths in modern Python. Appreciate you adding the extra safeguards anyway as defense-in-depth. Good catch on the stdlib behavior.