Skip to content

Disable rubyzip zip64 so Word can open generated court reports - #7098

Merged
compwron merged 1 commit into
mainfrom
fix-zip64-court-reports
Aug 7, 2026
Merged

Disable rubyzip zip64 so Word can open generated court reports#7098
compwron merged 1 commit into
mainfrom
fix-zip64-court-reports

Conversation

@compwron

@compwron compwron commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

What github issue is this PR for, if any?

Refs #7093

What changed, and why?

Generated court reports download fine and then Word refuses them:

The document "…docx" could not be opened. The file isn't in the correct format.

Root cause: the reports are being written as zip64.

rubyzip 3 flipped write_zip64_support to true by default — it was false in 2.x — and the caxlsx bump in e310472 (2026-04-13) floated us from rubyzip 2.4.1 to 3.x. Sablon builds reports with Zip::OutputStream.write_buffer, which streams entries without knowing their sizes up front, so with zip64 enabled every entry gets written with version-needed 45 and 0xFFFFFFFF size placeholders.

Microsoft Word rejects zip64 OOXML packages outright. unzip, rubyzip and the docx gem all read them happily, which is exactly why nothing caught it.

Confirmed against production — the blob is byte-for-byte intact coming out of Azure, and it is zip64 at rest:

b = CasaCase.find_by(case_number: "…").latest_court_report
b.byte_size                      # => 56340   (matches the file on disk exactly)
b.download[0, 26]
# => "PK\x03\x04-\x00\x00\x00\b\x00\x125\x06]\xE3\xEA2\xC6\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x13\x00"
#            ^^ 0x2d = 45, zip64      ^^^^^^^^^^^^^^^^^^^^^^^^ both size fields = zip64 placeholders

Nothing corrupts in transit. The file is written wrong at generation.

Zip.write_zip64_support = false restores the pre-April behaviour. Before / after on the same template:

before:  504b 0304 2d00 0000 0800 58be 055d 0074 2647 ffff ffff ffff ffff
after:   504b 0304 1400 0000 0800 b600 065d 0074 2647 b101 0000 9708 0000
                   ^^^^ 20, not 45                    ^^^^^^^^^^^^^^^^^^^ real sizes

which matches how Word itself writes the template (1400).

The setting is global rather than scoped to sablon because caxlsx has the same exposure — Excel dislikes zip64 for the same reason Word does. The tradeoff is no archives over 4GB or past 65,535 entries; court reports and spreadsheet exports are nowhere near either.

Note for whoever deploys: every court report already sitting in Azure was written as zip64 and stays unopenable. Users have to re-generate. Nothing to backfill — the next generate overwrites.

How is this tested? (please write rspec and jest tests!) 💖💪

Added a zip container format guard to spec/models/case_court_report_spec.rb asserting version-needed 20 and real entry sizes. Every existing assertion in that file inspects content via the docx gem, which parses zip64 without complaint — so a container-level assertion is the only kind that catches this.

Verified the guard actually fails on the defect, by removing the initializer and re-running:

expected: 20
     got: 45

expected: value != 4294967295
     got: 4294967295

Full runs, green:

$ bundle exec rspec spec/models/case_court_report_spec.rb
43 examples, 0 failures

$ bundle exec rspec spec/models/case_court_report_spec.rb spec/requests/case_court_reports_spec.rb
70 examples, 0 failures

I also ran all six shipped templates through sablon and validated each output — valid zip, no duplicate entries, every XML part passing strict Nokogiri parsing — to rule out the package being malformed in some other way first.

Screenshots please :)

No UI change. The visible difference is Word opening the file instead of refusing it.

rubyzip 3 flipped write_zip64_support to true by default (it was false in 2.x),
and the caxlsx bump floated us from rubyzip 2.4.1 to 3.x in April. Sablon builds
reports with Zip::OutputStream.write_buffer, which streams entries without
knowing their sizes up front, so with zip64 on every entry was written with
version-needed 45 and 0xFFFFFFFF size placeholders.

Word rejects zip64 OOXML packages outright — "The file isn't in the correct
format" — while unzip, rubyzip and the docx gem all read them happily. That is
why the whole suite stayed green while production handed users unopenable
files, across every org and template.

Confirmed against production: the blob downloads byte-for-byte intact, and its
first entry header reads 2d 00 ... ff ff ff ff ff ff ff ff. Nothing was corrupt
in transit; the file was written as zip64 at generation.

Add a guard asserting version-needed 20 and real entry sizes, since every
content-level assertion we have passes on a zip64 package.

Refs #7093

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added 🧪 Tests Tests ruby Touches Ruby code labels Aug 6, 2026
@compwron
compwron merged commit ae9e86f into main Aug 7, 2026
12 checks passed
@compwron
compwron deleted the fix-zip64-court-reports branch August 7, 2026 04:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ruby Touches Ruby code 🧪 Tests Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant