use pith_digest::Error;
use pith_inflate::Limits;
use pith_zip::ZipArchive;
fn le16(out: &mut Vec<u8>, v: u16) {
out.extend_from_slice(&v.to_le_bytes());
}
fn le32(out: &mut Vec<u8>, v: u32) {
out.extend_from_slice(&v.to_le_bytes());
}
fn one_member_archive(cs_override: Option<u32>) -> Vec<u8> {
let name = b"a.txt";
let body = b"payload bytes";
let crc = pith_digest::crc32(body);
let cs = body.len() as u32;
let us = body.len() as u32;
let mut out = Vec::new();
let local_offset = out.len() as u32;
le32(&mut out, 0x0403_4b50);
le16(&mut out, 20);
le16(&mut out, 0); le16(&mut out, 0); le16(&mut out, 0x7e21);
le16(&mut out, 0x0121);
le32(&mut out, crc);
le32(&mut out, cs);
le32(&mut out, us);
le16(&mut out, name.len() as u16);
le16(&mut out, 0);
out.extend_from_slice(name);
out.extend_from_slice(body);
let cd_offset = out.len() as u32;
le32(&mut out, 0x0201_4b50);
le16(&mut out, 20);
le16(&mut out, 20);
le16(&mut out, 0); le16(&mut out, 0); le16(&mut out, 0x7e21);
le16(&mut out, 0x0121);
le32(&mut out, crc);
le32(&mut out, cs_override.unwrap_or(cs));
le32(&mut out, us);
le16(&mut out, name.len() as u16);
le16(&mut out, 0); le16(&mut out, 0); le16(&mut out, 0); le16(&mut out, 0); le32(&mut out, 0); le32(&mut out, local_offset);
out.extend_from_slice(name);
let cd_size = out.len() as u32 - cd_offset;
le32(&mut out, 0x0605_4b50);
le16(&mut out, 0);
le16(&mut out, 0);
le16(&mut out, 1);
le16(&mut out, 1);
le32(&mut out, cd_size);
le32(&mut out, cd_offset);
le16(&mut out, 0);
out
}
#[test]
fn eocd_signature_cut_before_comment_length_is_truncated() {
assert_eq!(
ZipArchive::new(b"PK\x05\x06").unwrap_err(),
Error::truncated("end of central directory", 22, 4)
);
}
#[test]
fn zip64_central_entry_is_refused() {
let zip = one_member_archive(Some(0xFFFF_FFFF));
assert_eq!(
ZipArchive::new(&zip).unwrap_err(),
Error::Unsupported("zip64 central directory entry")
);
}
#[test]
fn padded_central_directory_is_bad_value() {
let mut zip = one_member_archive(None);
let eocd = zip
.windows(4)
.rposition(|w| w == [0x50, 0x4b, 0x05, 0x06])
.expect("archive has an eocd");
let junk = [0x00, 0x00, 0x00, 0x00];
zip.splice(eocd..eocd, junk);
let eocd_at = eocd + junk.len();
let size = u32::from_le_bytes(zip[eocd_at + 12..eocd_at + 16].try_into().expect("size"));
zip[eocd_at + 12..eocd_at + 16].copy_from_slice(&(size + 4).to_le_bytes());
assert_eq!(
ZipArchive::new(&zip).unwrap_err(),
Error::BadValue("zip central directory size")
);
}
#[test]
fn stored_entry_over_the_ceiling_is_too_large() {
let zip = one_member_archive(None);
let arc = ZipArchive::new(&zip).expect("parse");
let entry = arc.by_name("a.txt").expect("entry present");
let limits = Limits {
max_output: 4,
max_input: usize::MAX,
};
assert_eq!(
arc.extract_with(entry, &limits).unwrap_err(),
Error::too_large("zip entry", 4)
);
}