Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Descriptor check & Main Header locating #508

Merged
merged 3 commits into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions headers/entryHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ module.exports = function () {
}
},

get flags_desc() {
return (_flags & Constants.FLG_DESC) > 0;
},
set flags_desc(val) {
if (val) {
_flags |= Constants.FLG_DESC;
} else {
_flags &= ~Constants.FLG_DESC;
}
},

get method() {
return _method;
},
Expand Down
Binary file added test/assets/stream-nozip64.zip
Binary file not shown.
8 changes: 8 additions & 0 deletions test/methods/methods.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ describe("adm-zip.js - methods handling local files", () => {
].sort()
);
});

it("zip.extractAllTo(destination) - streamed file", () => {
const zip = new Zip("./test/assets/stream-nozip64.zip");
zip.extractAllTo(destination);
const files = walk(destination);

expect(files.sort()).to.deep.equal([pth.normalize("./test/xxx/lorem.txt")]);
});
});

describe(".extractAllToAsync - sync", () => {
Expand Down
41 changes: 36 additions & 5 deletions zipEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,46 @@ module.exports = function (/** object */ options, /*Buffer*/ input) {
}

function crc32OK(data) {
// if bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written
if ((_centralHeader.flags & 0x8) !== 0x8) {
// if bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the local header is written
if (!_centralHeader.flags_desc) {
if (Utils.crc32(data) !== _centralHeader.localHeader.crc) {
return false;
}
} else {
// @TODO: load and check data descriptor header
// The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure
// (optionally preceded by a 4-byte signature) immediately after the compressed data:
const descriptor = {};
const dataEndOffset = _centralHeader.realDataOffset + _centralHeader.compressedSize;
// no descriptor after compressed data, instead new local header
if (input.readUInt32LE(dataEndOffset) == Constants.LOCSIG || input.readUInt32LE(dataEndOffset) == Constants.CENSIG) {
throw new Error("ADM-ZIP: No descriptor present");
}

// get decriptor data
if (input.readUInt32LE(dataEndOffset) == Constants.EXTSIG) {
// descriptor with signature
descriptor.crc = input.readUInt32LE(dataEndOffset + Constants.EXTCRC);
descriptor.compressedSize = input.readUInt32LE(dataEndOffset + Constants.EXTSIZ);
descriptor.size = input.readUInt32LE(dataEndOffset + Constants.EXTLEN);
} else if (input.readUInt16LE(dataEndOffset + 12) === 0x4b50) {
// descriptor without signature (we check is new header starting where we expect)
descriptor.crc = input.readUInt32LE(dataEndOffset + Constants.EXTCRC - 4);
descriptor.compressedSize = input.readUInt32LE(dataEndOffset + Constants.EXTSIZ - 4);
descriptor.size = input.readUInt32LE(dataEndOffset + Constants.EXTLEN - 4);
} else {
throw new Error("ADM-ZIP: Unknown descriptor format");
}

// check data integrity
if (descriptor.compressedSize !== _centralHeader.compressedSize || descriptor.size !== _centralHeader.size || descriptor.crc !== _centralHeader.crc) {
throw new Error("ADM-ZIP: Descriptor data is malformed");
}
if (Utils.crc32(data) !== descriptor.crc) {
return false;
}

// @TODO: zip64 bit descriptor fields
// if bit 3 is set and any value in local header "zip64 Extended information" extra field are set 0 (place holder)
// then 64-bit descriptor format is used instead of 32-bit
// central header - "zip64 Extended information" extra field should store real values and not place holders
}
return true;
}
Expand Down
8 changes: 6 additions & 2 deletions zipFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) {
endOffset = -1, // Start offset of the END header
commentEnd = 0;

// option to search header form entire file
const trailingSpace = typeof opts.trailingSpace === "boolean" ? opts.trailingSpace : false;
if (trailingSpace) max = 0;

for (i; i >= n; i--) {
if (inBuffer[i] !== 0x50) continue; // quick check that the byte is 'P'
if (inBuffer.readUInt32LE(i) === Utils.Constants.ENDSIG) {
Expand All @@ -102,7 +106,7 @@ module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) {
}
}

if (!~endOffset) throw new Error(Utils.Errors.INVALID_FORMAT);
if (endOffset == -1) throw new Error(Utils.Errors.INVALID_FORMAT);

mainHeader.loadFromBinary(inBuffer.slice(endOffset, endStart));
if (mainHeader.commentLength) {
Expand Down Expand Up @@ -225,7 +229,7 @@ module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) {
const len = name.length;

entryList.forEach(function (zipEntry) {
if (zipEntry.entryName.substr(0, len) === name) {
if (zipEntry.entryName.startsWith(name)) {
list.push(zipEntry);
}
});
Expand Down