Skip to content

ZIP ExtractedFile.path diverges from the actual written location when entry names contain unsafe components #366

Description

@forkwright

Finding

ZipArchive::extract() sanitizes entry paths before writing (the zip crate strips leading slashes and resolves ..). The inventory loop that runs afterward does not apply equivalent sanitization: it joins the raw entry.name() to output_dir. For any entry whose name contains path-unsafe components, the recorded ExtractedFile.path will not match the actual filesystem location of the written file. The extraction pass and the inventory pass are independent and can disagree on the resulting path.

Evidence

crates/ergasia/src/extract/zip_extract.rs:27:

archive.extract(output_dir)

uses zip's sanitizing extractor. Lines 46-49:

let name = entry.name().to_string();
...
path: output_dir.join(name),

use the raw, unsanitized entry name. The path written to disk (sanitized) and the path recorded in the inventory (raw) are computed by two separate passes that can produce different results for the same entry.

Why this matters

A caller that consumes ExtractionResult.files to import, tag, or move the extracted files will silently skip every file whose recorded path does not match its real on-disk location. The extraction completes without error, yet the affected files are never processed — invisible data loss with no diagnostic. An archive can be crafted with such entry names to make specific files vanish from downstream handling while the operation still reports success, which under an adversarial threat model is an exploitable integrity gap.

Desired correction

Build the inventory by walking the actual filesystem under output_dir after extraction (the pattern already used in seven_zip.rs:collect_files) instead of re-reading archive entry names. Done when: a test with an entry named ../escape.flac shows the returned ExtractedFile.path still points inside output_dir and the file is accessible at that path.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions