diff --git a/lib/unzip-stream.js b/lib/unzip-stream.js index 5608184..b175070 100644 --- a/lib/unzip-stream.js +++ b/lib/unzip-stream.js @@ -6,35 +6,36 @@ var util = require('util'); var zlib = require('zlib'); var MatcherStream = require('./matcher-stream'); var Entry = require('./entry'); +const { STATES } = require('mongoose'); const states = { - STREAM_START: 0, - START: 1, - LOCAL_FILE_HEADER: 2, - LOCAL_FILE_HEADER_SUFFIX: 3, - FILE_DATA: 4, - FILE_DATA_END: 5, - DATA_DESCRIPTOR: 6, - CENTRAL_DIRECTORY_FILE_HEADER: 7, + STREAM_START: 0, + START: 1, + LOCAL_FILE_HEADER: 2, + LOCAL_FILE_HEADER_SUFFIX: 3, + FILE_DATA: 4, + FILE_DATA_END: 5, + DATA_DESCRIPTOR: 6, + CENTRAL_DIRECTORY_FILE_HEADER: 7, CENTRAL_DIRECTORY_FILE_HEADER_SUFFIX: 8, - CDIR64_END: 9, - CDIR64_END_DATA_SECTOR: 10, - CDIR64_LOCATOR: 11, - CENTRAL_DIRECTORY_END: 12, - CENTRAL_DIRECTORY_END_COMMENT: 13, - TRAILING_JUNK: 14, + CDIR64_END: 9, + CDIR64_END_DATA_SECTOR: 10, + CDIR64_LOCATOR: 11, + CENTRAL_DIRECTORY_END: 12, + CENTRAL_DIRECTORY_END_COMMENT: 13, + TRAILING_JUNK: 14, ERROR: 99 } const FOUR_GIGS = 4294967296; -const SIG_LOCAL_FILE_HEADER = 0x04034b50; -const SIG_DATA_DESCRIPTOR = 0x08074b50; -const SIG_CDIR_RECORD = 0x02014b50; -const SIG_CDIR64_RECORD_END = 0x06064b50; +const SIG_LOCAL_FILE_HEADER = 0x04034b50; +const SIG_DATA_DESCRIPTOR = 0x08074b50; +const SIG_CDIR_RECORD = 0x02014b50; +const SIG_CDIR64_RECORD_END = 0x06064b50; const SIG_CDIR64_LOCATOR_END = 0x07064b50; -const SIG_CDIR_RECORD_END = 0x06054b50; +const SIG_CDIR_RECORD_END = 0x06054b50; function UnzipStream(options) { if (!(this instanceof UnzipStream)) { @@ -149,7 +150,7 @@ UnzipStream.prototype.processDataChunk = function (chunk) { if (this.options.debug) { var sig = chunk.readUInt32LE(0); var asString; - try { asString = chunk.slice(0, 4).toString(); } catch (e) {} + try { asString = chunk.slice(0, 4).toString(); } catch (e) { } console.log("Unexpected signature in zip file: 0x" + sig.toString(16), '"' + asString + '", skipped', this.skippedBytes, 'bytes'); } this.emit("error", new Error(errMsg)); @@ -174,10 +175,10 @@ UnzipStream.prototype.processDataChunk = function (chunk) { if (extra.parsed.path && !isUtf8) { entry.path = extra.parsed.path; } - if (Number.isFinite(extra.parsed.uncompressedSize) && this.parsedEntity.uncompressedSize === FOUR_GIGS-1) { + if (Number.isFinite(extra.parsed.uncompressedSize) && this.parsedEntity.uncompressedSize === FOUR_GIGS - 1) { this.parsedEntity.uncompressedSize = extra.parsed.uncompressedSize; } - if (Number.isFinite(extra.parsed.compressedSize) && this.parsedEntity.compressedSize === FOUR_GIGS-1) { + if (Number.isFinite(extra.parsed.compressedSize) && this.parsedEntity.compressedSize === FOUR_GIGS - 1) { this.parsedEntity.compressedSize = extra.parsed.compressedSize; } } @@ -286,6 +287,15 @@ UnzipStream.prototype.processDataChunk = function (chunk) { UnzipStream.prototype._prepareOutStream = function (vars, entry) { var self = this; + var isVersionSupported = vars.versionsNeededToExtract <= 45; + var isEncrypted = (vars.flags & 0x01) || (vars.flags & 0x40); + if (isEncrypted || !isVersionSupported) { + var message = isEncrypted ? "Encrypted files are not supported!" + : ("Zip version " + Math.floor(vars.versionsNeededToExtract / 10) + "." + vars.versionsNeededToExtract % 10 + " is not supported"); + self.state = states.ERROR; + throw new Error(message); + } + var isDirectory = vars.uncompressedSize === 0 && /[\/\\]$/.test(entry.path); // protect against malicious zip files which want to extract to parent dirs entry.path = entry.path.replace(/^([/\\]*[.]+[/\\]+)*[/\\]*/, ""); @@ -297,8 +307,6 @@ UnzipStream.prototype._prepareOutStream = function (vars, entry) { entry.size = vars.uncompressedSize; } - var isVersionSupported = vars.versionsNeededToExtract <= 45; - this.outStreamInfo = { stream: null, limit: fileSizeKnown ? vars.compressedSize : -1, @@ -345,21 +353,6 @@ UnzipStream.prototype._prepareOutStream = function (vars, entry) { this.outStreamInfo.stream = new stream.PassThrough(); } - var isEncrypted = (vars.flags & 0x01) || (vars.flags & 0x40); - if (isEncrypted || !isVersionSupported) { - var message = isEncrypted ? "Encrypted files are not supported!" - : ("Zip version " + Math.floor(vars.versionsNeededToExtract / 10) + "." + vars.versionsNeededToExtract % 10 + " is not supported"); - - entry.skip = true; - setImmediate(() => { - entry.emit("error", new Error(message)); - }); - - // try to skip over this entry - this.outStreamInfo.stream.pipe(new Entry().autodrain()); - return; - } - var isCompressed = vars.compressionMethod > 0; if (isCompressed) { var inflater = zlib.createInflateRaw(); @@ -375,6 +368,8 @@ UnzipStream.prototype._prepareOutStream = function (vars, entry) { if (this._drainAllEntries) { entry.autodrain(); } + + return true; } UnzipStream.prototype._readFile = function (data) { @@ -414,7 +409,7 @@ UnzipStream.prototype._readExtraFields = function (data) { switch (vars.extraId) { case 0x0001: fieldType = "Zip64 extended information extra field"; - var z64vars = binary.parse(data.slice(index, index+vars.extraSize)) + var z64vars = binary.parse(data.slice(index, index + vars.extraSize)) .word64lu('uncompressedSize') .word64lu('compressedSize') .word64lu('offsetToLocalHeader') @@ -640,7 +635,7 @@ UnzipStream.prototype._decodeString = function (buffer, isUtf8) { return this.options.decodeString(buffer); } let result = ""; - for (var i=0; i 0) { - this.data = this.data.slice(consume); - if (this.data.length === 0) break; + try { + while ((consume = this.processDataChunk(this.data)) > 0) { + this.data = this.data.slice(consume); + if (this.data.length === 0) break; + } + } catch (error) { + return cb(error); } if (this.state === states.FILE_DATA) { @@ -707,7 +706,12 @@ UnzipStream.prototype._transform = function (chunk, encoding, cb) { } var startDataLength = self.data.length; - var done = function () { + var done = function (err) { + if (err) { + self.state = states.ERROR; + self.emit("error", err); + return; + } if (self.data.length > 0 && self.data.length < startDataLength) { startDataLength = self.data.length; self._parseOrOutput(encoding, done);