#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::io::Cursor;
use zip_core::{CompressionMethod, FormatError, ZipArchive, ZipCoreError};
#[allow(clippy::too_many_arguments)]
fn one_entry(
method: u16,
name: &[u8],
flags: u16,
comp: &[u8],
cd_csize: u32,
cd_usize: u32,
cd_lfh_offset: u32,
cd_extra: &[u8],
) -> Vec<u8> {
let mut o = Vec::new();
o.extend_from_slice(&[0x50, 0x4b, 0x03, 0x04]);
o.extend_from_slice(&20u16.to_le_bytes());
o.extend_from_slice(&flags.to_le_bytes());
o.extend_from_slice(&method.to_le_bytes());
o.extend_from_slice(&0u16.to_le_bytes());
o.extend_from_slice(&0u16.to_le_bytes());
o.extend_from_slice(&0u32.to_le_bytes());
o.extend_from_slice(&(comp.len() as u32).to_le_bytes());
o.extend_from_slice(&(comp.len() as u32).to_le_bytes());
o.extend_from_slice(&(name.len() as u16).to_le_bytes());
o.extend_from_slice(&0u16.to_le_bytes());
o.extend_from_slice(name);
o.extend_from_slice(comp);
let cd = o.len();
o.extend_from_slice(&[0x50, 0x4b, 0x01, 0x02]);
o.extend_from_slice(&20u16.to_le_bytes());
o.extend_from_slice(&20u16.to_le_bytes());
o.extend_from_slice(&flags.to_le_bytes());
o.extend_from_slice(&method.to_le_bytes());
o.extend_from_slice(&0u16.to_le_bytes());
o.extend_from_slice(&0u16.to_le_bytes());
o.extend_from_slice(&0u32.to_le_bytes());
o.extend_from_slice(&cd_csize.to_le_bytes());
o.extend_from_slice(&cd_usize.to_le_bytes());
o.extend_from_slice(&(name.len() as u16).to_le_bytes());
o.extend_from_slice(&(cd_extra.len() as u16).to_le_bytes());
o.extend_from_slice(&0u16.to_le_bytes());
o.extend_from_slice(&0u16.to_le_bytes());
o.extend_from_slice(&0u16.to_le_bytes());
o.extend_from_slice(&0u32.to_le_bytes());
o.extend_from_slice(&cd_lfh_offset.to_le_bytes());
o.extend_from_slice(name);
o.extend_from_slice(cd_extra);
let cd_size = o.len() - cd;
o.extend_from_slice(&[0x50, 0x4b, 0x05, 0x06]);
o.extend_from_slice(&0u16.to_le_bytes());
o.extend_from_slice(&0u16.to_le_bytes());
o.extend_from_slice(&1u16.to_le_bytes());
o.extend_from_slice(&1u16.to_le_bytes());
o.extend_from_slice(&(cd_size as u32).to_le_bytes());
o.extend_from_slice(&(cd as u32).to_le_bytes());
o.extend_from_slice(&0u16.to_le_bytes());
o
}
fn open(bytes: Vec<u8>) -> Result<ZipArchive<Cursor<Vec<u8>>>, ZipCoreError> {
ZipArchive::new(Cursor::new(bytes))
}
#[test]
fn directory_entry_and_accessors() {
let mut ar = open(one_entry(0, b"sub/", 0, b"", 0, 0, 0, &[])).unwrap();
let e = ar.by_index(0).unwrap();
assert!(e.is_dir());
assert_eq!(e.crc32(), 0);
assert_eq!(e.compressed_size(), 0);
}
#[test]
fn cp437_name_decoded_when_no_utf8_flag() {
let ar = open(one_entry(0, &[0xE1, b'.', b'b'], 0, b"", 0, 0, 0, &[])).unwrap();
assert!(ar.file_names().next().unwrap().starts_with('ß'));
}
#[test]
fn lfh_bad_signature_errors() {
let mut bytes = one_entry(0, b"f", 0, b"x", 1, 1, 0, &[]);
bytes[0] = 0x00; let mut ar = open(bytes).unwrap(); assert!(matches!(
ar.by_index(0).map(|_| ()),
Err(ZipCoreError::Format(FormatError::BadSignature { .. }))
));
}
#[test]
fn central_directory_out_of_range_errors() {
let mut bytes = one_entry(0, b"f", 0, b"x", 1, 1, 0, &[]);
let n = bytes.len();
bytes[n - 6..n - 2].copy_from_slice(&0x00FF_FFFFu32.to_le_bytes());
assert!(matches!(
open(bytes).map(|_| ()),
Err(ZipCoreError::Format(
FormatError::CentralDirOutOfRange { .. }
))
));
}
#[test]
fn central_dir_header_bad_signature_errors() {
let bytes = one_entry(0, b"f", 0, b"x", 1, 1, 0, &[]);
let mut b = bytes.clone();
let cd = b
.windows(4)
.position(|w| w == [0x50, 0x4b, 0x01, 0x02])
.unwrap();
b[cd + 1] = 0x00;
assert!(open(b).is_err());
}
#[test]
fn truncated_central_directory_errors() {
let mut bytes = one_entry(0, b"f", 0, b"x", 1, 1, 0, &[]);
let n = bytes.len();
bytes[n - 12..n - 10].copy_from_slice(&2u16.to_le_bytes());
bytes[n - 14..n - 12].copy_from_slice(&2u16.to_le_bytes()); assert!(open(bytes).is_err());
}
#[test]
fn zip64_locator_too_close_to_start() {
let mut eocd = Vec::new();
eocd.extend_from_slice(&[0x50, 0x4b, 0x05, 0x06]);
eocd.extend_from_slice(&[0u8; 12]);
eocd.extend_from_slice(&0xFFFF_FFFFu32.to_le_bytes()); eocd.extend_from_slice(&0u16.to_le_bytes());
assert!(matches!(
open(eocd).map(|_| ()),
Err(ZipCoreError::Format(FormatError::Zip64Unsupported))
));
}
#[test]
fn zip64_locator_bad_signature() {
let mut b = vec![0u8; 20];
b.extend_from_slice(&[0x50, 0x4b, 0x05, 0x06]);
b.extend_from_slice(&[0u8; 12]);
b.extend_from_slice(&0xFFFF_FFFFu32.to_le_bytes());
b.extend_from_slice(&0u16.to_le_bytes());
assert!(open(b).is_err());
}
#[test]
fn zip64_eocd_record_bad_signature() {
let mut b = vec![0u8; 8]; let loc_off = b.len();
b.extend_from_slice(&[0x50, 0x4b, 0x06, 0x07]); b.extend_from_slice(&0u32.to_le_bytes()); b.extend_from_slice(&0u64.to_le_bytes()); b.extend_from_slice(&1u32.to_le_bytes()); let _ = loc_off;
b.extend_from_slice(&[0x50, 0x4b, 0x05, 0x06]);
b.extend_from_slice(&[0u8; 12]);
b.extend_from_slice(&0xFFFF_FFFFu32.to_le_bytes());
b.extend_from_slice(&0u16.to_le_bytes());
assert!(open(b).is_err());
}
fn z64_extra(records: &[(u16, &[u8])]) -> Vec<u8> {
let mut v = Vec::new();
for (id, data) in records {
v.extend_from_slice(&id.to_le_bytes());
v.extend_from_slice(&(data.len() as u16).to_le_bytes());
v.extend_from_slice(data);
}
v
}
#[test]
fn zip64_extra_resolves_compressed_and_offset() {
let payload = b"hello";
let mut body = Vec::new();
body.extend_from_slice(&(payload.len() as u64).to_le_bytes());
body.extend_from_slice(&(payload.len() as u64).to_le_bytes());
body.extend_from_slice(&0u64.to_le_bytes()); let extra = z64_extra(&[(0x0001, &body)]);
let bytes = one_entry(
0,
b"f",
0,
payload,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
&extra,
);
let mut ar = open(bytes).unwrap();
let e = ar.by_name("f").unwrap();
assert_eq!(e.compressed_size(), payload.len() as u64);
assert_eq!(e.size(), payload.len() as u64);
}
#[test]
fn zip64_extra_skips_unrelated_record() {
let mut body = Vec::new();
body.extend_from_slice(&5u64.to_le_bytes()); let extra = z64_extra(&[(0x9999, &[1, 2, 3, 4]), (0x0001, &body)]);
let bytes = one_entry(0, b"f", 0, b"hello", 5, 0xFFFF_FFFF, 0, &extra);
let mut ar = open(bytes).unwrap();
assert_eq!(ar.by_name("f").unwrap().size(), 5);
}
#[test]
fn zip64_sentinel_without_extra_is_inconsistent() {
let extra = z64_extra(&[(0x9999, &[0, 0])]); let bytes = one_entry(0, b"f", 0, b"hello", 5, 0xFFFF_FFFF, 0, &extra);
assert!(matches!(
open(bytes).map(|_| ()),
Err(ZipCoreError::Format(FormatError::Zip64Inconsistent))
));
}
#[test]
fn unsupported_method_value_is_preserved() {
let mut ar = open(one_entry(99, b"f", 0, b"x", 1, 1, 0, &[])).unwrap();
assert!(matches!(
ar.by_index(0).map(|_| ()),
Err(ZipCoreError::UnsupportedMethod(CompressionMethod::Unknown(
99
)))
));
}
fn zip64_archive(total_entries: u64, record_sig_ok: bool) -> Vec<u8> {
let mut b = Vec::new();
let rec_off = b.len() as u64;
b.extend_from_slice(if record_sig_ok {
&[0x50, 0x4b, 0x06, 0x06]
} else {
&[0x00, 0x00, 0x00, 0x00]
});
b.extend_from_slice(&44u64.to_le_bytes()); b.extend_from_slice(&45u16.to_le_bytes());
b.extend_from_slice(&45u16.to_le_bytes());
b.extend_from_slice(&0u32.to_le_bytes()); b.extend_from_slice(&0u32.to_le_bytes()); b.extend_from_slice(&total_entries.to_le_bytes()); b.extend_from_slice(&total_entries.to_le_bytes()); b.extend_from_slice(&0u64.to_le_bytes()); b.extend_from_slice(&0u64.to_le_bytes()); b.extend_from_slice(&[0x50, 0x4b, 0x06, 0x07]); b.extend_from_slice(&0u32.to_le_bytes());
b.extend_from_slice(&rec_off.to_le_bytes());
b.extend_from_slice(&1u32.to_le_bytes());
b.extend_from_slice(&[0x50, 0x4b, 0x05, 0x06]);
b.extend_from_slice(&0u16.to_le_bytes()); b.extend_from_slice(&0u16.to_le_bytes()); b.extend_from_slice(&0xFFFFu16.to_le_bytes()); b.extend_from_slice(&0xFFFFu16.to_le_bytes()); b.extend_from_slice(&0u32.to_le_bytes()); b.extend_from_slice(&0xFFFF_FFFFu32.to_le_bytes()); b.extend_from_slice(&0u16.to_le_bytes()); b
}
#[test]
fn zip64_too_many_entries_rejected() {
assert!(matches!(
open(zip64_archive(20_000_000, true)).map(|_| ()),
Err(ZipCoreError::Format(FormatError::TooManyEntries(_)))
));
}
#[test]
fn zip64_eocd_record_wrong_signature_rejected() {
assert!(open(zip64_archive(1, false)).is_err());
}
#[test]
fn enclosed_name_rejects_nul_and_dot_only() {
let mut ar = open(one_entry(0, b"a\x00b", 0, b"", 0, 0, 0, &[])).unwrap();
assert_eq!(ar.by_index(0).unwrap().enclosed_name(), None); let mut ar = open(one_entry(0, b".", 0, b"", 0, 0, 0, &[])).unwrap();
assert_eq!(ar.by_index(0).unwrap().enclosed_name(), None); }