diff --git a/src/validation.rs b/src/validation.rs index aadf8f8..939eeda 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -88,6 +88,11 @@ impl FileValidator { "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | // .docx "application/vnd.openxmlformats-officedocument.presentationml.presentation" | // .pptx "application/vnd.ms-powerpoint" | // .ppt + // Some tooling (e.g. python-docx) emits OOXML zips with non-standard + // entry ordering, so `infer` reports `application/zip` instead of the + // specific OOXML mime. Pair this with the extension check in + // `is_compatible_type` to still reject random zips. + "application/zip" | "application/octet-stream" // Generic binary ) } @@ -96,6 +101,9 @@ impl FileValidator { detected == extension || (detected == "application/octet-stream" && self.is_supported_mime(extension)) || (extension == "application/octet-stream" && self.is_supported_mime(detected)) + // OOXML files are zips; trust the extension when content-detection only + // sees the underlying zip container. + || (detected == "application/zip" && self.is_supported_mime(extension)) } } diff --git a/tests/fixtures/test_document_rels_first.docx b/tests/fixtures/test_document_rels_first.docx new file mode 100644 index 0000000..aa61b8b Binary files /dev/null and b/tests/fixtures/test_document_rels_first.docx differ diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index e375b17..b2093fd 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -137,6 +137,40 @@ async fn test_successful_docx_conversion() { assert_eq!(&pdf_content[0..4], b"%PDF"); } +// docx files generated by python-docx (and other tooling) put _rels/.rels before +// [Content_Types].xml inside the zip, so `infer` falls back to application/zip rather +// than the OOXML mime. Validator must still accept these as long as the extension matches. +#[tokio::test] +async fn test_docx_with_nonstandard_zip_ordering() { + ensure_server_running(); + wait_for_server_ready().await; + let client = reqwest::Client::new(); + + let file_path = get_test_file_path("test_document_rels_first.docx"); + let file_content = fs::read(&file_path).expect("Failed to read fixture"); + + let form = multipart::Form::new() + .part( + "file_content", + multipart::Part::bytes(file_content) + .mime_str("application/octet-stream") + .unwrap() + .file_name("test_document_rels_first.docx"), + ) + .text("file_name", "test_document_rels_first.docx"); + + let response = client + .post(format!("{}/convert2/pdf", TEST_SERVER_URL)) + .multipart(form) + .send() + .await + .expect("Failed to make conversion request"); + + assert_eq!(response.status(), 200); + let pdf_content = response.bytes().await.expect("Failed to read PDF response"); + assert_eq!(&pdf_content[0..4], b"%PDF"); +} + #[tokio::test] async fn test_successful_xlsx_conversion() { ensure_server_running();