You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Harden ZIP streaming to reject repeated entries and other malformed ZIP files (#15136)
## Summary
uv will now reject ZIP files that meet any of the following conditions:
- Multiple local header entries exist for the same file with different
contents.
- A local header entry exists for a file that isn't included in the
end-of-central directory record.
- An entry exists in the end-of-central directory record that does not
have a corresponding local header.
- The ZIP file contains contents after the first end-of-central
directory record.
- The CRC32 doesn't match between the local file header and the
end-of-central directory record.
- The compressed size doesn't match between the local file header and
the end-of-central directory record.
- The uncompressed size doesn't match between the local file header and
the end-of-central directory record.
- The reported central directory offset (in the end-of-central-directory
header) does not match the actual offset.
- The reported ZIP64 end of central directory locator offset does not
match the actual offset.
We also validate the above for files with data descriptors, which we
previously ignored.
Wheels from the most recent releases of the top 15,000 packages on PyPI
have been confirmed to pass these checks, and PyPI will also reject ZIPs
under many of the same conditions (at upload time) in the future.
In rare cases, this validation can be disabled by setting
`UV_INSECURE_NO_ZIP_VALIDATION=1`. Any validations should be reported to
the uv issue tracker and to the upstream package maintainer.
#[error("ZIP file contains an end-of-central-directory record entry, but no local file header for: {} ({offset}", path.display())]
44
+
MissingLocalFileHeader{path:PathBuf,offset:u64},
45
+
#[error("ZIP file uses conflicting paths for the local file header at {} (got {}, expected {})", offset, local_path.display(), central_directory_path.display())]
46
+
ConflictingPaths{
47
+
offset:u64,
48
+
local_path:PathBuf,
49
+
central_directory_path:PathBuf,
50
+
},
51
+
#[error("ZIP file uses conflicting checksums for the local file header and central-directory record (got {local_crc32}, expected {central_directory_crc32}) for: {} ({offset})", path.display())]
52
+
ConflictingChecksums{
53
+
path:PathBuf,
54
+
offset:u64,
55
+
local_crc32:u32,
56
+
central_directory_crc32:u32,
57
+
},
58
+
#[error("ZIP file uses conflicting compressed sizes for the local file header and central-directory record (got {local_compressed_size}, expected {central_directory_compressed_size}) for: {} ({offset})", path.display())]
59
+
ConflictingCompressedSizes{
60
+
path:PathBuf,
61
+
offset:u64,
62
+
local_compressed_size:u64,
63
+
central_directory_compressed_size:u64,
64
+
},
65
+
#[error("ZIP file uses conflicting uncompressed sizes for the local file header and central-directory record (got {local_uncompressed_size}, expected {central_directory_uncompressed_size}) for: {} ({offset})", path.display())]
66
+
ConflictingUncompressedSizes{
67
+
path:PathBuf,
68
+
offset:u64,
69
+
local_uncompressed_size:u64,
70
+
central_directory_uncompressed_size:u64,
71
+
},
72
+
#[error("ZIP file contains trailing contents after the end-of-central-directory record")]
73
+
TrailingContents,
74
+
#[error(
75
+
"ZIP file reports a number of entries in the central directory that conflicts with the actual number of entries (got {actual}, expected {expected})"
0 commit comments