Description
SafePath::validate does not normalize \ to / before path component analysis. The upstream sevenz_rust2::safe_join (removed when switching to ArchiveReader::for_each_entries in #375) did replace('\\', '/') prior to validation.
Impact
On Unix: an entry named ..\..\x is treated as one literal path component and extracted as a file with a backslash-containing name inside the destination directory. Not a security escape — the file lands inside dest — but the behavior silently changed from rejected (under the old safe_join) to accepted (under the current code).
On Windows: Path::components() splits on \, so traversal via ..\..\x is already caught by the .. check in SafePath::validate. No regression.
Reproduction Steps
- Create a 7z archive with an entry named
..\..\x
- Extract with
allow_absolute_paths = false
- On Unix: extraction succeeds and writes
dest/..\..\x (literal backslash name)
- Expected: rejection (PathTraversal or SecurityViolation)
Expected Behavior
Embedded \ in 7z entry names should be normalized to / before path validation, consistent with what safe_join did, and consistent with how Windows handles paths natively.
Actual Behavior
On Unix, ..\..\x is accepted and a file with a backslash-containing name is created inside the destination directory.
Environment
- Version: 0.5.0
- OS: Linux / macOS (Windows unaffected)
- Archive format: 7z
- Binding: all (core-level issue)
Proposed Fix
In the 7z adapter (formats/sevenz.rs), normalize entry names before constructing the PathBuf:
let entry_path = std::path::PathBuf::from(entry.name.replace('\\', "/"));
Add a unit test with a ..\..\x entry name verifying rejection on all platforms.
Notes
Description
SafePath::validatedoes not normalize\to/before path component analysis. The upstreamsevenz_rust2::safe_join(removed when switching toArchiveReader::for_each_entriesin #375) didreplace('\\', '/')prior to validation.Impact
On Unix: an entry named
..\..\xis treated as one literal path component and extracted as a file with a backslash-containing name inside the destination directory. Not a security escape — the file lands inside dest — but the behavior silently changed from rejected (under the oldsafe_join) to accepted (under the current code).On Windows:
Path::components()splits on\, so traversal via..\..\xis already caught by the..check inSafePath::validate. No regression.Reproduction Steps
..\..\xallow_absolute_paths = falsedest/..\..\x(literal backslash name)Expected Behavior
Embedded
\in 7z entry names should be normalized to/before path validation, consistent with whatsafe_joindid, and consistent with how Windows handles paths natively.Actual Behavior
On Unix,
..\..\xis accepted and a file with a backslash-containing name is created inside the destination directory.Environment
Proposed Fix
In the 7z adapter (
formats/sevenz.rs), normalize entry names before constructing thePathBuf:Add a unit test with a
..\..\xentry name verifying rejection on all platforms.Notes
ArchiveReader::for_each_entriesswitch