From 861ed2d8ef118a2281bd70776c3bd089446c77b9 Mon Sep 17 00:00:00 2001 From: Matt Sutton Date: Thu, 7 May 2026 02:07:28 -0400 Subject: [PATCH] Accept application/zip for OOXML content detection `infer` only reports the OOXML mime when the zip's first entry is [Content_Types].xml. Tooling like python-docx writes _rels/.rels first, so the validator was rejecting otherwise-valid docx/xlsx/pptx with "Unknown or unsupported file type" before LibreOffice ever saw them. Allow application/zip through `is_supported_mime`, and treat it as compatible with any supported OOXML extension. Random zips renamed to .docx still fail at the LibreOffice load step. Added tests/fixtures/test_document_rels_first.docx (a re-pack of the existing test_document.docx with _rels/.rels first) and a regression test that converts it end-to-end. --- src/validation.rs | 8 +++++ tests/fixtures/test_document_rels_first.docx | Bin 0 -> 1216 bytes tests/integration_tests.rs | 34 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 tests/fixtures/test_document_rels_first.docx 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 0000000000000000000000000000000000000000..aa61b8ba0e55927b56a9d584a411b42e5e6249a3 GIT binary patch literal 1216 zcmWIWW@Zs#U|`^2I57KZl&|u%xm$od14af0ZXg|Bl$ukluLmM~`>pv783?%io^$AK z*bz=vCk;QTmMLPb%n{#?ZS1h$bXI-)aaLAGksR~g*7+vB-%X5nCU@M5WGjtibkv@6 zCCqRK&+@Z!9zJ)x?5w+a#Ck4lbuB(NbNQRj$=@cXboBBpoARgel7^az&9rli&rZL{ zdN_ERW8nFtQ?-}eUUlo@hm#H46e`0KE_t&poNhWH@#7Ew25EQQ4=;|KUEph9w9n3H zCVOk=!NaVcPpf0obS~VPysm4TegKN+3gme^KLI_p59m2zARXN6?HFP^N*TXJB*;*E)Kp8S5cbJyf1E*54zwl_}%J=gtq-EFC+a`2(U z+&NyJ-@PWzyTE+aFfk#u*eJ|h9l65{9u*#_XM4jS~E&Ub;&WV03 zNqB4H>*gM0Gv}`HhRG8qZI+h?0Z)0cd<+Fi@&t$@~_|i zs6kZRb?f{AV9?kAgGdlam**Fy=%?f-m*xV)2^2t~{RjD)fT`epool|W=f!42mJbU) zzGAgsa7onTh2G1siTk(nN;vD^t-O22=kw*`-yZSySs!wGHz|bcz$~A3U&Z%VORJ|e z?R8sR?WFO9MaTcCn!K^|*V9Z@f-(~}Z=A69yYn)U>$(wVIe8l%zV$qpCBG#5ipM#g zzN`d3#%8w*TTglF)*@UZpRz1cC*jBH)*YsiEA+J!^!4_~r8~*L4^TLCd%3Z~ zBd?Bgp?BhcubNso#k)x6d064Ezq|i1p?Y_f3FD4Bpf~3Nu?)<+kTj3%X>j@vJ!8nn zq$tq(@Luo*-=|Gt$7ZB%zs)RRd!R)&QJJSj(Jfzp=`{WNd*