use proptest::prelude::*;
use std::io::{Cursor, Write};
fn corrupted_bytes() -> impl Strategy<Value = Vec<u8>> {
prop::collection::vec(any::<u8>(), 0..4096)
}
fn fake_zip_with_garbage() -> impl Strategy<Value = Vec<u8>> {
prop::collection::vec(any::<u8>(), 0..2048).prop_map(|mut extra| {
let mut bytes = vec![0x50, 0x4B, 0x03, 0x04]; bytes.append(&mut extra);
bytes
})
}
fn truncated_docx_zip() -> impl Strategy<Value = Vec<u8>> {
let valid_zip = make_minimal_docx_zip();
let len = valid_zip.len();
(0..len).prop_map(move |cut| valid_zip[..cut].to_vec())
}
fn make_minimal_docx_zip() -> Vec<u8> {
let xml = br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p><w:r><w:t>test</w:t></w:r></w:p>
</w:body>
</w:document>"#;
let mut buf = Vec::new();
{
let cursor = Cursor::new(&mut buf);
let mut zip = zip::ZipWriter::new(cursor);
let options = zip::write::SimpleFileOptions::default()
.compression_method(zip::CompressionMethod::Stored);
zip.start_file("word/document.xml", options).unwrap();
zip.write_all(xml).unwrap();
zip.finish().unwrap();
}
buf
}
fn write_temp_file(data: &[u8]) -> tempfile::NamedTempFile {
let mut tmp = tempfile::NamedTempFile::new().expect("failed to create temp file");
tmp.write_all(data).expect("failed to write temp file");
tmp
}
proptest! {
#[test]
fn fuzz_from_reader_does_not_panic_on_garbage(bytes in corrupted_bytes()) {
let cursor = Cursor::new(bytes);
let mut reader = easydoc_reader::DocxSaxReader::from_reader(cursor);
let _ = reader.read_blocks();
}
#[test]
fn fuzz_from_path_does_not_panic_on_garbage(bytes in corrupted_bytes()) {
let tmp = write_temp_file(&bytes);
let _ = easydoc_reader::DocxSaxReader::from_path(tmp.path());
}
#[test]
fn fuzz_from_path_does_not_panic_on_fake_zip(bytes in fake_zip_with_garbage()) {
let tmp = write_temp_file(&bytes);
let _ = easydoc_reader::DocxSaxReader::from_path(tmp.path());
}
#[test]
fn fuzz_from_path_does_not_panic_on_truncated_zip(bytes in truncated_docx_zip()) {
let tmp = write_temp_file(&bytes);
let _ = easydoc_reader::DocxSaxReader::from_path(tmp.path());
}
#[test]
fn fuzz_read_document_does_not_panic_on_arbitrary_path(path in "\\PC{1,200}") {
let _ = easydoc_reader::read_document(std::path::Path::new(&path));
}
#[test]
fn fuzz_ssrf_guard_does_not_panic_on_arbitrary_url(url in "\\PC{0,512}") {
let guard = easydoc_reader::security::SsrfGuard::new();
let _ = guard.check_url(&url);
}
#[test]
fn fuzz_ssrf_guard_permissive_does_not_panic(url in "\\PC{0,512}") {
let guard = easydoc_reader::security::SsrfGuard::permissive();
let _ = guard.check_url(&url);
}
#[test]
fn fuzz_view_as_does_not_panic_on_garbage(
bytes in corrupted_bytes(),
mode in prop_oneof![
Just(easydoc_reader::ViewMode::Plain),
Just(easydoc_reader::ViewMode::Annotated),
Just(easydoc_reader::ViewMode::Stats),
(0u8..7).prop_map(|l| easydoc_reader::ViewMode::Outline { max_level: l }),
],
) {
let tmp = write_temp_file(&bytes);
if let Ok(content) = easydoc_reader::read_document(tmp.path()) {
let _ = easydoc_reader::render_view(&content, &mode);
}
}
}