use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use zip::write::SimpleFileOptions;
use zip::CompressionMethod;
use zip::ZipWriter;
use openpack::{Limits, OpenPack, OpenPackError};
struct Scratch {
_tmp: tempfile::TempDir,
path: PathBuf,
}
impl Scratch {
fn new(suffix: &str) -> Self {
let tmp = tempfile::tempdir().expect("tempdir");
let path = tmp.path().join(format!("archive.{suffix}"));
Self { _tmp: tmp, path }
}
}
fn write_zip(path: &std::path::Path, entries: &[(&str, &[u8], CompressionMethod)]) {
let file = File::create(path).unwrap();
let mut zip = ZipWriter::new(file);
for (name, data, comp) in entries {
let options = SimpleFileOptions::default().compression_method(*comp);
zip.start_file(*name, options).unwrap();
zip.write_all(data).unwrap();
}
zip.finish().unwrap();
}
#[test]
fn zip_bomb_ratio_check_in_read_entry() {
let archive = Scratch::new("zip");
let payload = vec![0u8; 1024 * 1024 * 10];
write_zip(
&archive.path,
&[("bomb.txt", &payload, CompressionMethod::Deflated)],
);
let mut limits = Limits::default();
limits.max_compression_ratio = 5.0;
limits.max_entry_uncompressed_size = 20 * 1024 * 1024;
limits.max_total_uncompressed_size = 20 * 1024 * 1024;
let pack = OpenPack::open(&archive.path, limits).unwrap();
let err = pack.read_entry("bomb.txt").unwrap_err();
assert!(
matches!(err, OpenPackError::LimitExceeded(ref msg) if msg.contains("compression ratio limit")),
"Expected compression ratio limit error, got {:?}",
err
);
}
#[test]
fn path_traversal_in_read_entry() {
let archive = Scratch::new("zip");
write_zip(
&archive.path,
&[("../../etc/passwd", b"secret", CompressionMethod::Stored)],
);
let pack = OpenPack::open_default(&archive.path).unwrap();
let err = pack.read_entry("../../etc/passwd").unwrap_err();
assert!(
matches!(err, OpenPackError::ZipSlip(_)),
"Expected ZipSlip error on read_entry"
);
let contains_err = pack.contains("../../etc/passwd").unwrap_err();
assert!(
matches!(contains_err, OpenPackError::ZipSlip(_)),
"Expected ZipSlip error on contains"
);
}
#[test]
fn path_traversal_in_extract_all_to() {
let archive = Scratch::new("zip");
write_zip(
&archive.path,
&[("../../etc/passwd", b"secret", CompressionMethod::Stored)],
);
let pack = OpenPack::open_default(&archive.path).unwrap();
let extract_dir = tempfile::tempdir().unwrap();
let err = pack.extract_all_to(extract_dir.path()).unwrap_err();
assert!(
matches!(err, OpenPackError::ZipSlip(_)),
"Expected ZipSlip error on extract_all_to"
);
}