Skip to content

Fix corrupted zip when round-tripping entries with a data descriptor (#555) - #564

Merged
cthackers merged 1 commit into
cthackers:masterfrom
JohnJunior:fix/issue-555-data-descriptor
Jun 29, 2026
Merged

cthackers merged 1 commit into
cthackers:masterfrom
JohnJunior:fix/issue-555-data-descriptor

Conversation

@JohnJunior

@JohnJunior JohnJunior commented Jun 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #555.

Reading a zip and writing it back out could produce a file that adm-zip itself (and strict unzip implementations) refused to read, failing with ADM-ZIP: No descriptor present.

It affects entries whose general-purpose bit flag has bit 3 (0x08, "data descriptor") set — the way MS Office .docx, .NET packaging and streaming archivers write their files. For those entries the real crc-32 and sizes live
in a trailing data descriptor after the compressed data, and the local header holds zeros.

On write, adm-zip rebuilds a full local header with the real crc/sizes, but it kept bit 3 set while emitting no trailing descriptor. The output then claims "a descriptor follows" when none does; on the next read crc32OK()
looks for it right after the data, hits the next header signature instead, and throws.

Fix

compressToBuffer always knows the crc/sizes at write time and never emits a descriptor, so bit 3 must not be set on output. Clear it in both the local and central header serialization (headers/entryHeader.js):

data.writeUInt16LE(_flags & ~Constants.FLG_DESC, Constants.LOCFLG);
// ...
data.writeUInt16LE(_flags & ~Constants.FLG_DESC, Constants.CENFLG);

The in-memory flags are left untouched — only the emitted bytes are normalized, so reading an entry's metadata after parsing is unchanged.

Test

Adds test/issue_555 with a sample.docx whose entries use data descriptors. The test runs the exact flow from the issue (writeZipPromise → toBufferPromise → getEntries() → getData()). It throws No descriptor present without the fix and passes with it. Full suite: 92 passing.

Notes

A .docx only triggers this if its producer actually used data descriptors —
some do, some don't, which is why the report looked intermittent.

…thackers#555)

Entries whose general-purpose bit flag has bit 3 (0x08, "data descriptor")
set store their crc-32 and sizes in a trailing descriptor, not in the local
header. On write, adm-zip rebuilds full local headers with the real crc/sizes
but kept bit 3 set while emitting no trailing descriptor. The result was a
zip that adm-zip itself (and strict unzip implementations) could not read
back, failing with "ADM-ZIP: No descriptor present".
Since compressToBuffer always knows the crc/sizes at write time and never
emits a descriptor, clear bit 3 in both the local and central header
serialization. The in-memory flags are left untouched; only the emitted
bytes are normalized.
Adds test/issue_555 with a sample.docx whose entries use data descriptors,
reproducing the reported read -> write -> read failure.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Corrupted ZIP being created from simple read then write

2 participants