From c64ac147df6f1d97a3b0a7680c68024642772c4f Mon Sep 17 00:00:00 2001 From: 5saviahv Date: Mon, 16 Nov 2020 16:44:19 +0200 Subject: [PATCH 1/8] LICENSE filename in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e035aa8..ec892c5 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "util", "zipEntry.js", "zipFile.js", - "MIT-LICENSE.txt" + "LICENSE" ], "main": "adm-zip.js", "repository": { From 63ed6e24b62eb4a1d566b0903a88111108f8dec4 Mon Sep 17 00:00:00 2001 From: 5saviahv Date: Wed, 18 Nov 2020 15:00:49 +0200 Subject: [PATCH 2/8] Detect and throw error with encrypted files --- zipEntry.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zipEntry.js b/zipEntry.js index 473610d..01f702e 100644 --- a/zipEntry.js +++ b/zipEntry.js @@ -54,6 +54,11 @@ module.exports = function (/*Buffer*/input) { return compressedData; } + if (_entryHeader.encripted){ + if (async && callback) callback(Buffer.alloc(0), Utils.Errors.UNKNOWN_METHOD); + throw new Error(Utils.Errors.UNKNOWN_METHOD); + } + var data = Buffer.alloc(_entryHeader.size); switch (_entryHeader.method) { From 003d4cfe0e3ae23ac0f86f0bced45ca52e7f80af Mon Sep 17 00:00:00 2001 From: 5saviahv Date: Wed, 18 Nov 2020 16:26:44 +0200 Subject: [PATCH 3/8] Added ZipCrypto decrypting ability --- adm-zip.js | 12 +++---- methods/index.js | 3 +- methods/zipcrypto.js | 77 ++++++++++++++++++++++++++++++++++++++++++++ zipEntry.js | 8 +++-- 4 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 methods/zipcrypto.js diff --git a/adm-zip.js b/adm-zip.js index 38c104c..a34ef60 100644 --- a/adm-zip.js +++ b/adm-zip.js @@ -63,9 +63,9 @@ module.exports = function (/**String*/input) { * * @return Buffer or Null in case of error */ - readFile: function (/**Object*/entry) { + readFile: function (/**Object*/entry, /*String, Buffer*/pass) { var item = getEntry(entry); - return item && item.getData() || null; + return item && item.getData(pass) || null; }, /** @@ -482,7 +482,7 @@ module.exports = function (/**String*/input) { * Test the archive * */ - test: function () { + test: function (pass) { if (!_zip) { return false; } @@ -492,7 +492,7 @@ module.exports = function (/**String*/input) { if (entry.isDirectory) { continue; } - var content = _zip.entries[entry].getData(); + var content = _zip.entries[entry].getData(pass); if (!content) { return false; } @@ -510,7 +510,7 @@ module.exports = function (/**String*/input) { * @param overwrite If the file already exists at the target path, the file will be overwriten if this is true. * Default is FALSE */ - extractAllTo: function (/**String*/targetPath, /**Boolean*/overwrite) { + extractAllTo: function (/**String*/targetPath, /**Boolean*/overwrite, /*String, Buffer*/pass) { overwrite = overwrite || false; if (!_zip) { throw new Error(Utils.Errors.NO_ZIP); @@ -521,7 +521,7 @@ module.exports = function (/**String*/input) { Utils.makeDir(entryName); return; } - var content = entry.getData(); + var content = entry.getData(pass); if (!content) { throw new Error(Utils.Errors.CANT_EXTRACT_FILE); } diff --git a/methods/index.js b/methods/index.js index ddcbba6..0994aac 100644 --- a/methods/index.js +++ b/methods/index.js @@ -1,2 +1,3 @@ exports.Deflater = require("./deflater"); -exports.Inflater = require("./inflater"); \ No newline at end of file +exports.Inflater = require("./inflater"); +exports.ZipCrypto = require("./zipcrypto"); \ No newline at end of file diff --git a/methods/zipcrypto.js b/methods/zipcrypto.js new file mode 100644 index 0000000..0cf8d32 --- /dev/null +++ b/methods/zipcrypto.js @@ -0,0 +1,77 @@ +// generate CRC32 lookup table +const crctable = (new Uint32Array(256)).map((t,crc)=>{ + for(let j=0;j<8;j++){ + if (0 !== (crc & 1)){ + crc = (crc >>> 1) ^ 0xEDB88320 + }else{ + crc >>>= 1 + } + } + return crc>>>0; +}); + +function make_decrypter(/*Buffer*/pwd){ + // C-style uInt32 Multiply + const uMul = (a,b) => Math.imul(a, b) >>> 0; + // Initialize keys with default values + const keys = new Uint32Array([0x12345678, 0x23456789, 0x34567890]); + // crc32 byte update + const crc32update = (pCrc32, bval) => { + return crctable[(pCrc32 ^ bval) & 0xff] ^ (pCrc32 >>> 8); + } + // update keys with byteValues + const updateKeys = (byteValue) => { + keys[0] = crc32update(keys[0], byteValue); + keys[1] += keys[0] & 0xff; + keys[1] = uMul(keys[1], 134775813) + 1; + keys[2] = crc32update(keys[2], keys[1] >>> 24); + } + + // 1. Stage initialize key + const pass = (Buffer.isBuffer(pwd)) ? pwd : Buffer.from(pwd); + for(let i=0; i< pass.length; i++){ + updateKeys(pass[i]); + } + + // return decrypter function + return function (/*Buffer*/data){ + if (!Buffer.isBuffer(data)){ + throw 'decrypter needs Buffer' + } + // result - we create new Buffer for results + const result = Buffer.alloc(data.length); + let pos = 0; + // process input data + for(let c of data){ + const k = (keys[2] | 2) >>> 0; // key + c ^= (uMul(k, k^1) >> 8) & 0xff; // decode + result[pos++] = c; // Save Value + updateKeys(c); // update keys with decoded byte + } + return result; + } +} + +function decrypt(/*Buffer*/ data, /*Object*/header, /*String, Buffer*/ pwd){ + if (!data || !Buffer.isBuffer(data) || data.length < 12) { + return Buffer.alloc(0); + } + + // We Initialize and generate decrypting function + const decrypter = make_decrypter(pwd); + + // check - for testing password + const check = header.crc >>> 24; + // decrypt salt what is always 12 bytes and is a part of file content + const testbyte = decrypter(data.slice(0, 12))[11]; + + // does password meet expectations + if (check !== testbyte){ + throw 'ADM-ZIP: Wrong Password'; + } + + // decode content + return decrypter(data.slice(12)); +} + +module.exports = {decrypt}; diff --git a/zipEntry.js b/zipEntry.js index 01f702e..d5dddd0 100644 --- a/zipEntry.js +++ b/zipEntry.js @@ -34,7 +34,7 @@ module.exports = function (/*Buffer*/input) { return true; } - function decompress(/*Boolean*/async, /*Function*/callback, /*String*/pass) { + function decompress(/*Boolean*/async, /*Function*/callback, /*String, Buffer*/pass) { if(typeof callback === 'undefined' && typeof async === 'string') { pass=async; async=void 0; @@ -55,8 +55,10 @@ module.exports = function (/*Buffer*/input) { } if (_entryHeader.encripted){ - if (async && callback) callback(Buffer.alloc(0), Utils.Errors.UNKNOWN_METHOD); - throw new Error(Utils.Errors.UNKNOWN_METHOD); + if ('string' !== typeof pass && !Buffer.isBuffer(pass)){ + throw new Error('ADM-ZIP: Incompatible password parameter'); + } + compressedData = Methods.ZipCrypto.decrypt(compressedData, _entryHeader, pass); } var data = Buffer.alloc(_entryHeader.size); From dde4f518b033dfb05c5068a984a885ed9c2e313b Mon Sep 17 00:00:00 2001 From: 5saviahv Date: Thu, 19 Nov 2020 15:28:06 +0200 Subject: [PATCH 4/8] Node v6 --- package.json | 2 +- util/fattr.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index ec892c5..6e84372 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "url": "https://github.com/cthackers/adm-zip.git" }, "engines": { - "node": ">=0.3.0" + "node": ">=6.0" }, "devDependencies": { "chai": "^4.1.2", diff --git a/util/fattr.js b/util/fattr.js index b56fbd7..be47676 100644 --- a/util/fattr.js +++ b/util/fattr.js @@ -26,8 +26,8 @@ module.exports = function(/*String*/path) { _obj.directory = _stat.isDirectory(); _obj.mtime = _stat.mtime; _obj.atime = _stat.atime; - _obj.executable = !!(1 & parseInt ((_stat.mode & parseInt ("777", 8)).toString (8)[0])); - _obj.readonly = !!(2 & parseInt ((_stat.mode & parseInt ("777", 8)).toString (8)[0])); + _obj.executable = (0o111 & _stat.mode) != 0; // file is executable who ever har right not just owner + _obj.readonly = (0o200 & _stat.mode) == 0; // readonly if owner has no write right _obj.hidden = pth.basename(_path)[0] === "."; } else { console.warn("Invalid path: " + _path) From bc0f594a9a6f77e7b0f8d5c839d521e5f64000c2 Mon Sep 17 00:00:00 2001 From: 5saviahv Date: Thu, 19 Nov 2020 15:35:33 +0200 Subject: [PATCH 5/8] Deflate needs min V2.0 --- headers/entryHeader.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/headers/entryHeader.js b/headers/entryHeader.js index f27ebf5..01b01c8 100644 --- a/headers/entryHeader.js +++ b/headers/entryHeader.js @@ -3,7 +3,7 @@ var Utils = require("../util"), /* The central directory file header */ module.exports = function () { - var _verMade = 0x0A, + var _verMade = 0x14, _version = 0x0A, _flags = 0, _method = 0, @@ -20,6 +20,15 @@ module.exports = function () { _attr = 0, _offset = 0; + switch(process.platform){ + case 'win32': + _verMade |= 0x0A00; + case 'darwin': + _verMade |= 0x1300; + default: + _verMade |= 0x0300; + } + var _dataHeader = {}; function setTime(val) { @@ -47,7 +56,16 @@ module.exports = function () { set flags (val) { _flags = val; }, get method () { return _method; }, - set method (val) { _method = val; }, + set method (val) { + switch (val){ + case Constants.STORED: + this.version = 10; + case Constants.DEFLATED: + default: + this.version = 20; + } + _method = val; + }, get time () { return new Date( ((_time >> 25) & 0x7f) + 1980, From dc81063bfb5205fc0c283dc7dd82ee2876bbbcba Mon Sep 17 00:00:00 2001 From: 5saviahv Date: Thu, 19 Nov 2020 15:51:34 +0200 Subject: [PATCH 6/8] modified addLocalFile method --- adm-zip.js | 50 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/adm-zip.js b/adm-zip.js index a34ef60..dac0c5b 100644 --- a/adm-zip.js +++ b/adm-zip.js @@ -208,23 +208,30 @@ module.exports = function (/**String*/input) { * @param zipPath Optional path inside the zip * @param zipName Optional name for the file */ - addLocalFile: function (/**String*/localPath, /**String=*/zipPath, /**String=*/zipName) { + addLocalFile: function (/**String*/localPath, /**String=*/zipPath, /**String=*/zipName, /**String*/comment) { if (fs.existsSync(localPath)) { + // prepare zippath if (zipPath) { + // convert windows file separators zipPath = zipPath.split("\\").join("/"); + // add separator if it wasnt given if (zipPath.charAt(zipPath.length - 1) !== "/") { zipPath += "/"; } } else { zipPath = ""; } + // p - local file name var p = localPath.split("\\").join("/").split("/").pop(); - if (zipName) { - this.addFile(zipPath + zipName, fs.readFileSync(localPath), "", 0) - } else { - this.addFile(zipPath + p, fs.readFileSync(localPath), "", 0) - } + // add file name into zippath + zipPath += (zipName) ? zipPath : p; + + // read file attributes + const _attr = fs.statSync(localPath); + + // add file into zip file + this.addFile(zipPath, fs.readFileSync(localPath), comment, _attr) } else { throw new Error(Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath)); } @@ -376,19 +383,38 @@ module.exports = function (/**String*/input) { * @param attr */ addFile: function (/**String*/entryName, /**Buffer*/content, /**String*/comment, /**Number*/attr) { + // prepare new entry var entry = new ZipEntry(); entry.entryName = entryName; entry.comment = comment || ""; - if (!attr) { - if (entry.isDirectory) { - attr = (0o40755 << 16) | 0x10; // (permissions drwxr-xr-x) + (MS-DOS directory flag) - } else { - attr = 0o644 << 16; // permissions -r-wr--r-- + var isStat = ('object' === typeof attr) && (attr instanceof fs.Stats); + + // last modification time from file stats + if (isStat){ + entry.header.time = attr.mtime; + } + + // Set file attribute + var fileattr = (entry.isDirectory) ? 0x10 : 0; // (MS-DOS directory flag) + + // extended attributes field for Unix + if('win32' !== process.platform){ + // set file type either S_IFDIR / S_IFREG + var unix = (entry.isDirectory) ? 0x4000 : 0x8000; + + if (isStat) { // File attributes from file stats + unix |= (0xfff & attr.mode) + }else if ('number' === typeof attr){ // attr from given attr values + unix |= (0xfff & attr); + }else{ // Default values: + unix |= (entry.isDirectory) ? 0o755 : 0o644; // permissions (drwxr-xr-x) or (-r-wr--r--) } + + fileattr = (fileattr | (unix << 16)) >>> 0; // add attributes } - entry.attr = attr; + entry.attr = fileattr; entry.setData(content); _zip.setEntry(entry); From e11492943f3e6d222ede9eb148e4936d3b8d911c Mon Sep 17 00:00:00 2001 From: 5saviahv Date: Thu, 19 Nov 2020 15:55:31 +0200 Subject: [PATCH 7/8] typo --- adm-zip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adm-zip.js b/adm-zip.js index dac0c5b..3a18897 100644 --- a/adm-zip.js +++ b/adm-zip.js @@ -225,7 +225,7 @@ module.exports = function (/**String*/input) { var p = localPath.split("\\").join("/").split("/").pop(); // add file name into zippath - zipPath += (zipName) ? zipPath : p; + zipPath += (zipName) ? zipName : p; // read file attributes const _attr = fs.statSync(localPath); From 7db0eda579f38c780136a2248234cb4605717fb7 Mon Sep 17 00:00:00 2001 From: 5saviahv Date: Thu, 19 Nov 2020 20:40:32 +0200 Subject: [PATCH 8/8] modified addLocalFolder method --- adm-zip.js | 113 +++++++++++++++++++++++++---------------------------- 1 file changed, 54 insertions(+), 59 deletions(-) diff --git a/adm-zip.js b/adm-zip.js index 3a18897..4f9847b 100644 --- a/adm-zip.js +++ b/adm-zip.js @@ -56,6 +56,16 @@ module.exports = function (/**String*/input) { return null; } + function fixPath(zipPath){ + // convert windows file separators + zipPath = zipPath.split("\\").join("/"); + // add separator if it wasnt given + if (zipPath.charAt(zipPath.length - 1) !== "/") { + zipPath += "/"; + } + return zipPath; + } + return { /** * Extracts the given entry from the archive and returns the content as a Buffer object @@ -210,17 +220,9 @@ module.exports = function (/**String*/input) { */ addLocalFile: function (/**String*/localPath, /**String=*/zipPath, /**String=*/zipName, /**String*/comment) { if (fs.existsSync(localPath)) { - // prepare zippath - if (zipPath) { - // convert windows file separators - zipPath = zipPath.split("\\").join("/"); - // add separator if it wasnt given - if (zipPath.charAt(zipPath.length - 1) !== "/") { - zipPath += "/"; - } - } else { - zipPath = ""; - } + // fix ZipPath + zipPath = (zipPath) ? fixPath(zipPath) : ""; + // p - local file name var p = localPath.split("\\").join("/").split("/").pop(); @@ -245,54 +247,47 @@ module.exports = function (/**String*/input) { * @param filter optional RegExp or Function if files match will * be included. */ - addLocalFolder: function (/**String*/localPath, /**String=*/zipPath, /**=RegExp|Function*/filter) { - if (filter === undefined) { - filter = function () { - return true; - }; - } else if (filter instanceof RegExp) { - filter = function (filter) { - return function (filename) { - return filter.test(filename); - } - }(filter); - } - - if (zipPath) { - zipPath = zipPath.split("\\").join("/"); - if (zipPath.charAt(zipPath.length - 1) !== "/") { - zipPath += "/"; - } - } else { - zipPath = ""; - } - // normalize the path first - localPath = pth.normalize(localPath); - localPath = localPath.split("\\").join("/"); //windows fix - if (localPath.charAt(localPath.length - 1) !== "/") - localPath += "/"; - - if (fs.existsSync(localPath)) { - - var items = Utils.findFiles(localPath), - self = this; - - if (items.length) { - items.forEach(function (path) { - var p = path.split("\\").join("/").replace(new RegExp(localPath.replace(/(\(|\)|\$)/g, '\\$1'), 'i'), ""); //windows fix - if (filter(p)) { - if (p.charAt(p.length - 1) !== "/") { - self.addFile(zipPath + p, fs.readFileSync(path), "", 0) - } else { - self.addFile(zipPath + p, Buffer.alloc(0), "", 0) - } - } - }); - } - } else { - throw new Error(Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath)); - } - }, + addLocalFolder: function (/**String*/localPath, /**String=*/zipPath, /**=RegExp|Function*/filter) { + // Prepare filter + if (filter instanceof RegExp) { // if filter is RegExp wrap it + filter = (function (rx){ + return function (filename) { + return rx.test(filename); + } + })(filter); + } else if ('function' !== typeof filter) { // if filter is not function we will replace it + filter = function () { + return true; + }; + } + + // fix ZipPath + zipPath = (zipPath) ? fixPath(zipPath) : ""; + + // normalize the path first + localPath = pth.normalize(localPath); + + if (fs.existsSync(localPath)) { + + var items = Utils.findFiles(localPath), + self = this; + + if (items.length) { + items.forEach(function (filepath) { + var p = pth.relative(localPath, filepath).split("\\").join("/"); //windows fix + if (filter(p)) { + if (filepath.charAt(filepath.length - 1) !== pth.sep) { + self.addFile(zipPath + p, fs.readFileSync(filepath), "", fs.statSync(filepath)); + } else { + self.addFile(zipPath + p + '/', Buffer.alloc(0), "", 0); + } + } + }); + } + } else { + throw new Error(Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath)); + } + }, /** * Asynchronous addLocalFile