From 85d28e2b036d3803b16c003dcf98732b80a1bb82 Mon Sep 17 00:00:00 2001 From: 5saviahv <5saviahv@users.noreply.github.com> Date: Sun, 9 Jun 2024 00:17:42 +0300 Subject: [PATCH 1/5] addLocalFolder update --- adm-zip.js | 95 ++++++++++++++++++++++++++------------------------- util/utils.js | 9 ++--- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/adm-zip.js b/adm-zip.js index 51e37d7..049912a 100644 --- a/adm-zip.js +++ b/adm-zip.js @@ -84,6 +84,28 @@ module.exports = function (/**String*/ input, /** object */ options) { return join(".", normalize(sep + zipPath.split("\\").join(sep) + sep)); } + function filenameFilter(filterfn) { + if (filterfn instanceof RegExp) { + // if filter is RegExp wrap it + return (function (rx) { + return function (filename) { + return rx.test(filename); + }; + })(filterfn); + } else if ("function" !== typeof filterfn) { + // if filter is not function we will replace it + return () => true; + } + return filterfn; + } + + // keep last character on folders + const relativePath = (local, entry) => { + let lastChar = entry.slice(-1); + lastChar = lastChar === filetools.sep ? filetools.sep : ""; + return pth.relative(local, entry) + lastChar; + }; + return { /** * Extracts the given entry from the archive and returns the content as a Buffer object @@ -234,17 +256,18 @@ module.exports = function (/**String*/ input, /** object */ options) { /** * Adds a file from the disk to the archive * - * @param localPath File to add to zip - * @param zipPath Optional path inside the zip - * @param zipName Optional name for the file + * @param {string} localPath File to add to zip + * @param {string} [zipPath] Optional path inside the zip + * @param {string} [zipName] Optional name for the file + * @param {string} [comment] Optional file comment */ - addLocalFile: function (/**String*/ localPath, /**String=*/ zipPath, /**String=*/ zipName, /**String*/ comment) { + addLocalFile: function (localPath, zipPath, zipName, comment) { if (filetools.fs.existsSync(localPath)) { // fix ZipPath zipPath = zipPath ? fixPath(zipPath) : ""; // p - local file name - var p = localPath.split("\\").join("/").split("/").pop(); + const p = pth.win32.basename(pth.win32.normalize(localPath)); // add file name into zippath zipPath += zipName ? zipName : p; @@ -252,8 +275,14 @@ module.exports = function (/**String*/ input, /** object */ options) { // read file attributes const _attr = filetools.fs.statSync(localPath); + // get file content + const data = _attr.isFile() ? filetools.fs.readFileSync(localPath) : Buffer.alloc(0); + + // if folder + if (_attr.isDirectory()) zipPath += filetools.sep; + // add file into zip file - this.addFile(zipPath, filetools.fs.readFileSync(localPath), comment, _attr); + this.addFile(zipPath, data, comment, _attr); } else { throw new Error(Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath)); } @@ -262,27 +291,13 @@ module.exports = function (/**String*/ input, /** object */ options) { /** * Adds a local directory and all its nested files and directories to the archive * - * @param localPath - * @param zipPath optional path inside zip - * @param filter optional RegExp or Function if files match will - * be included. - * @param {number | object} attr - number as unix file permissions, object as filesystem Stats object + * @param {string} localPath - local path to the folder + * @param {string} [zipPath] - optional path inside zip + * @param {(RegExp|function)} [filter] - optional RegExp or Function if files match will be included. */ - addLocalFolder: function (/**String*/ localPath, /**String=*/ zipPath, /**=RegExp|Function*/ filter, /**=number|object*/ attr) { + addLocalFolder: function (localPath, zipPath, 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; - }; - } + filter = filenameFilter(filter); // fix ZipPath zipPath = zipPath ? fixPath(zipPath) : ""; @@ -295,17 +310,12 @@ module.exports = function (/**String*/ input, /** object */ options) { const self = this; if (items.length) { - items.forEach(function (filepath) { - var p = pth.relative(localPath, filepath).split("\\").join("/"); //windows fix + for (const filepath of items) { + const p = pth.join(zipPath, relativePath(localPath, filepath)); if (filter(p)) { - var stats = filetools.fs.statSync(filepath); - if (stats.isFile()) { - self.addFile(zipPath + p, filetools.fs.readFileSync(filepath), "", attr ? attr : stats); - } else { - self.addFile(zipPath + p + "/", Buffer.alloc(0), "", attr ? attr : stats); - } + self.addLocalFile(filepath, pth.dirname(p)); } - }); + } } } else { throw new Error(Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath)); @@ -313,7 +323,7 @@ module.exports = function (/**String*/ input, /** object */ options) { }, /** - * Asynchronous addLocalFile + * Asynchronous addLocalFolder * @param localPath * @param callback * @param zipPath optional path inside zip @@ -321,17 +331,8 @@ module.exports = function (/**String*/ input, /** object */ options) { * be included. */ addLocalFolderAsync: function (/*String*/ localPath, /*Function*/ callback, /*String*/ zipPath, /*RegExp|Function*/ filter) { - if (filter instanceof RegExp) { - filter = (function (rx) { - return function (filename) { - return rx.test(filename); - }; - })(filter); - } else if ("function" !== typeof filter) { - filter = function () { - return true; - }; - } + // Prepare filter + filter = filenameFilter(filter); // fix ZipPath zipPath = zipPath ? fixPath(zipPath) : ""; @@ -353,7 +354,7 @@ module.exports = function (/**String*/ input, /** object */ options) { i += 1; if (i < items.length) { var filepath = items[i]; - var p = pth.relative(localPath, filepath).split("\\").join("/"); //windows fix + var p = relativePath(localPath, filepath).split("\\").join("/"); //windows fix p = p .normalize("NFD") .replace(/[\u0300-\u036f]/g, "") diff --git a/util/utils.js b/util/utils.js index 7758419..9b196ae 100644 --- a/util/utils.js +++ b/util/utils.js @@ -153,13 +153,14 @@ Utils.prototype.findFiles = function (/*String*/ path) { } let files = []; self.fs.readdirSync(dir).forEach(function (file) { - var path = pth.join(dir, file); - - if (self.fs.statSync(path).isDirectory() && recursive) files = files.concat(findSync(path, pattern, recursive)); + const path = pth.join(dir, file); + const stat = self.fs.statSync(path); if (!pattern || pattern.test(path)) { - files.push(pth.normalize(path) + (self.fs.statSync(path).isDirectory() ? self.sep : "")); + files.push(pth.normalize(path) + (stat.isDirectory() ? self.sep : "")); } + + if (stat.isDirectory() && recursive) files = files.concat(findSync(path, pattern, recursive)); }); return files; } From 0b914707abca64acfce2fd1e7ec3d61b06472ed4 Mon Sep 17 00:00:00 2001 From: 5saviahv <5saviahv@users.noreply.github.com> Date: Sun, 9 Jun 2024 00:21:07 +0300 Subject: [PATCH 2/5] added addLocalFolderAsync2 --- adm-zip.js | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++ util/utils.js | 41 ++++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/adm-zip.js b/adm-zip.js index 049912a..fb45b6a 100644 --- a/adm-zip.js +++ b/adm-zip.js @@ -288,6 +288,54 @@ module.exports = function (/**String*/ input, /** object */ options) { } }, + /** + * Callback for showing if everything was done. + * + * @callback doneCallback + * @param {Error} err - Error object + * @param {boolean} done - was request fully completed + */ + + /** + * Adds a file from the disk to the archive + * + * @param {(object|string)} options - options object, if it is string it us used as localPath. + * @param {string} options.localPath - Local path to the file. + * @param {string} [options.comment] - Optional file comment. + * @param {string} [options.zipPath] - Optional path inside the zip + * @param {string} [options.zipName] - Optional name for the file + * @param {doneCallback} callback - The callback that handles the response. + */ + addLocalFileAsync: function (options, callback) { + options = typeof options === "object" ? options : { localPath: options }; + const localPath = pth.resolve(options.localPath); + const { comment } = options; + let { zipPath, zipName } = options; + const self = this; + + filetools.fs.stat(localPath, function (err, stats) { + if (err) return callback(err, false); + // fix ZipPath + zipPath = zipPath ? fixPath(zipPath) : ""; + // p - local file name + const p = pth.win32.basename(pth.win32.normalize(localPath)); + // add file name into zippath + zipPath += zipName ? zipName : p; + + if (stats.isFile()) { + filetools.fs.readFile(localPath, function (err, data) { + if (err) return callback(err, false); + self.addFile(zipPath, data, comment, stats); + return setImmediate(callback, undefined, true); + }); + } else if (stats.isDirectory()) { + zipPath += filetools.sep; + self.addFile(zipPath, Buffer.alloc(0), comment, stats); + return setImmediate(callback, undefined, true); + } + }); + }, + /** * Adds a local directory and all its nested files and directories to the archive * @@ -392,6 +440,86 @@ module.exports = function (/**String*/ input, /** object */ options) { }, /** + * Adds a local directory and all its nested files and directories to the archive + * + * @param {object | string} options - options object, if it is string it us used as localPath. + * @param {string} options.localPath - Local path to the folder. + * @param {string} [options.zipPath] - optional path inside zip. + * @param {RegExp|function} [options.filter] - optional RegExp or Function if files match will be included. + * @param {function|string} [options.namefix] - optional function to help fix filename + * @param {doneCallback} callback - The callback that handles the response. + * + */ + addLocalFolderAsync2: function (/*String*/ options, /*Function*/ callback) { + const self = this; + options = typeof options === "object" ? options : { localPath: options }; + localPath = pth.resolve(fixPath(options.localPath)); + let { zipPath, filter, namefix } = options; + + if (filter instanceof RegExp) { + filter = (function (rx) { + return function (filename) { + return rx.test(filename); + }; + })(filter); + } else if ("function" !== typeof filter) { + filter = function () { + return true; + }; + } + + // fix ZipPath + zipPath = zipPath ? fixPath(zipPath) : ""; + + // Check Namefix function + if (namefix == "latin1") { + namefix = (str) => + str + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/[^\x20-\x7E]/g, ""); // accent fix (latin1 characers only) + } + + if (typeof namefix !== "function") namefix = (str) => str; + + // internal, create relative path + fix the name + const relPathFix = (entry) => pth.join(zipPath, namefix(relativePath(localPath, entry))); + const fileNameFix = (entry) => pth.win32.basename(pth.win32.normalize(namefix(entry))); + + filetools.fs.open(localPath, "r", function (err) { + if (err && err.code === "ENOENT") { + callback(undefined, Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath)); + } else if (err) { + callback(undefined, err); + } else { + filetools.findFilesAsync(localPath, function (err, fileEntries) { + if (err) return callback(err); + fileEntries = fileEntries.filter((dir) => filter(relPathFix(dir))); + if (!fileEntries.length) callback(undefined, false); + + setImmediate( + fileEntries.reverse().reduce(function (next, entry) { + return function (err, done) { + if (err || done === false) return setImmediate(next, err, false); + + self.addLocalFileAsync( + { + localPath: entry, + zipPath: pth.dirname(relPathFix(entry)), + zipName: fileNameFix(entry) + }, + next + ); + }; + }, callback) + ); + }); + } + }); + }, + + /** + * Adds a local directory and all its nested files and directories to the archive * * @param {string} localPath - path where files will be extracted * @param {object} props - optional properties diff --git a/util/utils.js b/util/utils.js index 9b196ae..784ae0a 100644 --- a/util/utils.js +++ b/util/utils.js @@ -168,6 +168,47 @@ Utils.prototype.findFiles = function (/*String*/ path) { return findSync(path, undefined, true); }; +/** + * Callback for showing if everything was done. + * + * @callback filelistCallback + * @param {Error} err - Error object + * @param {string[]} list - was request fully completed + */ + +/** + * + * @param {string} dir + * @param {filelistCallback} cb + */ +Utils.prototype.findFilesAsync = function (dir, cb) { + const self = this; + let results = []; + self.fs.readdir(dir, function (err, list) { + if (err) return cb(err); + let list_length = list.length; + if (!list_length) return cb(null, results); + list.forEach(function (file) { + file = pth.join(dir, file); + self.fs.stat(file, function (err, stat) { + if (err) return cb(err); + if (stat) { + results.push(pth.normalize(file) + (stat.isDirectory() ? self.sep : "")); + if (stat.isDirectory()) { + self.findFilesAsync(file, function (err, res) { + if (err) return cb(err); + results = results.concat(res); + if (!--list_length) cb(null, results); + }); + } else { + if (!--list_length) cb(null, results); + } + } + }); + }); + }); +}; + Utils.prototype.getAttributes = function () {}; Utils.prototype.setAttributes = function () {}; From 21111add461a7bba7cfd123950cc77c2ea2bd6f1 Mon Sep 17 00:00:00 2001 From: 5saviahv <5saviahv@users.noreply.github.com> Date: Sun, 9 Jun 2024 00:21:56 +0300 Subject: [PATCH 3/5] update addLocalFolderPromise --- adm-zip.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/adm-zip.js b/adm-zip.js index fb45b6a..317a7ec 100644 --- a/adm-zip.js +++ b/adm-zip.js @@ -523,21 +523,16 @@ module.exports = function (/**String*/ input, /** object */ options) { * * @param {string} localPath - path where files will be extracted * @param {object} props - optional properties - * @param {string} props.zipPath - optional path inside zip - * @param {regexp, function} props.filter - RegExp or Function if files match will be included. + * @param {string} [props.zipPath] - optional path inside zip + * @param {RegExp|function} [props.filter] - optional RegExp or Function if files match will be included. + * @param {function|string} [props.namefix] - optional function to help fix filename */ addLocalFolderPromise: function (/*String*/ localPath, /* object */ props) { return new Promise((resolve, reject) => { - const { filter, zipPath } = Object.assign({}, props); - this.addLocalFolderAsync( - localPath, - (done, err) => { - if (err) reject(err); - if (done) resolve(this); - }, - zipPath, - filter - ); + this.addLocalFolderAsync2(Object.assign({ localPath }, props), (err, done) => { + if (err) reject(err); + if (done) resolve(this); + }); }); }, From d8bca720e409a062d40b60c0899a96a2103b6639 Mon Sep 17 00:00:00 2001 From: 5saviahv <5saviahv@users.noreply.github.com> Date: Sun, 9 Jun 2024 00:24:39 +0300 Subject: [PATCH 4/5] include tests for addLocalFolder2 --- test/methods/methods.test.js | 577 +++++++++++++++++++++++++++++++++++ test/mocha.js | 161 ---------- 2 files changed, 577 insertions(+), 161 deletions(-) create mode 100644 test/methods/methods.test.js diff --git a/test/methods/methods.test.js b/test/methods/methods.test.js new file mode 100644 index 0000000..81a0cd8 --- /dev/null +++ b/test/methods/methods.test.js @@ -0,0 +1,577 @@ +const { expect } = require("chai"); +//const Attr = require("../util").FileAttr; +const Zip = require("../../adm-zip"); +const pth = require("path"); +const fs = require("fs"); +const rimraf = require("rimraf"); +const Utils = require("../../util/utils"); + +describe("adm-zip.js - methods handling local files", () => { + const destination = "./test/xxx"; + const testFileFolderList = [ + { name: "subfolder1/subfolder2/zipEntry1.txt", content: "zipEntry1" }, + { name: "subfolder1/subfolder2/subfolder3/zipEntry2.txt", content: "zipEntry2" }, + { name: "subfolder1/subfolder2/subfolder3/zipEntry3.txt", content: "zipEntry3" }, + { name: "subfolder1/subfolder2/subfolder3/subfolder4/" } + ]; + const testFileFileList = [ + { name: "folder/zipEntry1.txt", content: "zipEntry1" }, + { name: "folder/zipEntry2.txt", content: "zipEntry2" }, + { name: "folder/zipEntry3.txt", content: "zipEntry3" }, + { name: "folder/zipEntry4.txt", content: "zipEntry4" }, + { name: "folder/subfolder1/" }, + { name: "folder/subfolder2/" }, + { name: "folder/subfolder3/" } + ]; + + // clean up folder content + afterEach((done) => rimraf(destination, done)); + + describe(".extractAllTo() - sync", () => { + it("zip.extractAllTo(destination)", () => { + const zip = new Zip("./test/assets/ultra.zip"); + zip.extractAllTo(destination); + const files = walk(destination); + + expect(files.sort()).to.deep.equal( + [ + pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), + pth.normalize("./test/xxx/attributes_test/blank file.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), + pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") + ].sort() + ); + }); + }); + + describe(".extractAllToAsync - sync", () => { + it("zip.extractAllToAsync(destination)", (done) => { + const zip = new Zip("./test/assets/ultra.zip"); + zip.extractAllToAsync(destination, (error) => { + const files = walk(destination); + expect(files.sort()).to.deep.equal( + [ + pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), + pth.normalize("./test/xxx/attributes_test/blank file.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), + pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") + ].sort() + ); + done(); + }); + }); + + it("zip.extractAllToAsync(destination) [Promise]", function () { + const zip = new Zip("./test/assets/ultra.zip"); + // note the return + return zip.extractAllToAsync(destination).then(function (data) { + const files = walk(destination); + expect(files.sort()).to.deep.equal( + [ + pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), + pth.normalize("./test/xxx/attributes_test/blank file.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), + pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") + ].sort() + ); + }); // no catch, it'll figure it out since the promise is rejected + }); + + it("zip.extractAllToAsync(destination, false, false, callback)", (done) => { + const zip = new Zip("./test/assets/ultra.zip"); + zip.extractAllToAsync(destination, false, false, (error) => { + const files = walk(destination); + expect(files.sort()).to.deep.equal( + [ + pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), + pth.normalize("./test/xxx/attributes_test/blank file.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), + pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") + ].sort() + ); + done(); + }); + }); + + it("zip.extractAllToAsync(destination, false, false) [Promise]", function () { + const zip = new Zip("./test/assets/ultra.zip"); + // note the return + return zip.extractAllToAsync(destination, false, false).then(function (data) { + const files = walk(destination); + expect(files.sort()).to.deep.equal( + [ + pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), + pth.normalize("./test/xxx/attributes_test/blank file.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), + pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") + ].sort() + ); + }); // no catch, it'll figure it out since the promise is rejected + }); + + it("zip.extractAllToAsync(destination, false, callback)", (done) => { + const zip = new Zip("./test/assets/ultra.zip"); + zip.extractAllToAsync(destination, false, (error) => { + const files = walk(destination); + expect(files.sort()).to.deep.equal( + [ + pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), + pth.normalize("./test/xxx/attributes_test/blank file.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), + pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") + ].sort() + ); + done(); + }); + }); + + it("zip.extractAllToAsync(destination, false) [Promise]", () => { + const zip = new Zip("./test/assets/ultra.zip"); + // note the return + return zip.extractAllToAsync(destination, false).then(function (data) { + const files = walk(destination); + expect(files.sort()).to.deep.equal( + [ + pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), + pth.normalize("./test/xxx/attributes_test/blank file.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), + pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") + ].sort() + ); + }); // no catch, it'll figure it out since the promise is rejected + }); + }); + + describe(".extractEntryTo() - sync", () => { + it("zip.extractEntryTo(entry, destination, false, true)", () => { + const zip = new Zip("./test/assets/ultra.zip"); + var zipEntries = zip.getEntries(); + zipEntries.forEach((e) => zip.extractEntryTo(e, destination, false, true)); + + const files = walk(destination); + expect(files.sort()).to.deep.equal( + [ + pth.normalize("./test/xxx/blank file.txt"), + pth.normalize("./test/xxx/hidden.txt"), + pth.normalize("./test/xxx/hidden_readonly.txt"), + pth.normalize("./test/xxx/New Text Document.txt"), + pth.normalize("./test/xxx/readonly.txt"), + pth.normalize("./test/xxx/somefile.txt") + ].sort() + ); + }); + + it("zip.extractEntryTo(entry, destination, true, true)", () => { + const zip = new Zip("./test/assets/ultra.zip"); + var zipEntries = zip.getEntries(); + zipEntries.forEach((e) => zip.extractEntryTo(e, destination, true, true)); + + const files = walk(destination); + expect(files.sort()).to.deep.equal( + [ + pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), + pth.normalize("./test/xxx/attributes_test/blank file.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), + pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), + pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") + ].sort() + ); + }); + }); + + describe(".addLocalFolder() - sync", () => { + beforeEach(() => { + genFiles(testFileFolderList, destination); + }); + + it("zip.addLocalFolder(destination)", () => { + const zip = new Zip({ noSort: true }); + zip.addLocalFolder(destination); + zip.toBuffer(); + + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = [ + "subfolder1/", + "subfolder1/subfolder2/", + "subfolder1/subfolder2/subfolder3/", + "subfolder1/subfolder2/subfolder3/subfolder4/", + "subfolder1/subfolder2/subfolder3/zipEntry2.txt", + "subfolder1/subfolder2/subfolder3/zipEntry3.txt", + "subfolder1/subfolder2/zipEntry1.txt" + ]; + + expect(zip1Entries).to.deep.equal(expected); + }); + + it("zip.addLocalFolder(destination, zipPath)", () => { + const zip = new Zip(); + zip.addLocalFolder(destination, "parent"); + zip.toBuffer(); + + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = [ + "parent/subfolder1/", + "parent/subfolder1/subfolder2/", + "parent/subfolder1/subfolder2/subfolder3/", + "parent/subfolder1/subfolder2/subfolder3/subfolder4/", + "parent/subfolder1/subfolder2/subfolder3/zipEntry2.txt", + "parent/subfolder1/subfolder2/subfolder3/zipEntry3.txt", + "parent/subfolder1/subfolder2/zipEntry1.txt" + ].sort(); + + expect(zip1Entries).to.deep.equal(expected); + }); + + it("zip.addLocalFolder(destination, '', filter)", () => { + const zip = new Zip(); + const filter = /zipEntry[23]\.txt/; + zip.addLocalFolder(destination, "", filter); + zip.toBuffer(); + + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = ["subfolder1/subfolder2/subfolder3/zipEntry2.txt", "subfolder1/subfolder2/subfolder3/zipEntry3.txt"].sort(); + + expect(zip1Entries).to.deep.equal(expected); + }); + + it("zip.addLocalFolder(destination, '', filter)", () => { + const zip = new Zip(); + const filter = (str) => str.slice(-1) === "/"; + zip.addLocalFolder(destination, "", filter); + zip.toBuffer(); + + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = ["subfolder1/", "subfolder1/subfolder2/", "subfolder1/subfolder2/subfolder3/", "subfolder1/subfolder2/subfolder3/subfolder4/"].sort(); + + expect(zip1Entries).to.deep.equal(expected); + }); + }); + + describe(".addLocalFileAsync() - async", () => { + beforeEach(() => { + genFiles(testFileFileList, destination); + }); + + it("zip.addLocalFileAsync({ localPath, comment, zipPath }, callback)", (done) => { + const zip = new Zip(); + const zipPath = "folder"; + const fileComment = "file Comment"; + const list1 = testFileFileList.map((c) => c.name); + list1.sort(); + zip.addZipComment(fileComment); + + setImmediate( + list1.reverse().reduce( + function (next, file) { + return function (err, done) { + if (err) next(err, false); + + const localPath = pth.resolve(destination, file); + const comment = pth.basename(file); + + zip.addLocalFileAsync({ localPath, comment, zipPath }, function (err, done) { + if (err) next(err, false); + + setImmediate(next, undefined, true); + }); + }; + }, + function (err) { + if (err) done(err); + + const zip1Entries = zip.getEntries().map((e) => e.entryName); + const zip1Comment = zip.getEntries().map((e) => e.comment); + + const expected1 = list1; + const expected2 = list1.map((n) => pth.basename(n)); + + expect(zip1Entries.sort()).to.deep.equal(expected1.sort()); + expect(zip1Comment.sort()).to.deep.equal(expected2.sort()); + expect(zip.getZipComment()).to.equal(fileComment); + + done(); + } + ) + ); + }); + }); + + describe(".addLocalFolderAsync2() - async", () => { + beforeEach(() => { + genFiles(testFileFolderList, destination); + }); + + it("zip.addLocalFolderAsync2(destination, callback)", (done) => { + const zip = new Zip(); + zip.addLocalFolderAsync2(destination, (error) => { + if (error) done(false); + + zip.toBuffer(function () { + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = [ + "subfolder1/", + "subfolder1/subfolder2/", + "subfolder1/subfolder2/subfolder3/", + "subfolder1/subfolder2/zipEntry1.txt", + "subfolder1/subfolder2/subfolder3/subfolder4/", + "subfolder1/subfolder2/subfolder3/zipEntry2.txt", + "subfolder1/subfolder2/subfolder3/zipEntry3.txt" + ]; + + expect(zip1Entries).to.deep.equal(expected.sort()); + done(); + }); + }); + }); + + it("zip.addLocalFolderAsync2({localPath}, callback)", (done) => { + const zip = new Zip(); + zip.addLocalFolderAsync2({ localPath: destination }, (error) => { + if (error) done(false); + + zip.toBuffer(function () { + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = [ + "subfolder1/", + "subfolder1/subfolder2/", + "subfolder1/subfolder2/subfolder3/", + "subfolder1/subfolder2/subfolder3/subfolder4/", + "subfolder1/subfolder2/subfolder3/zipEntry2.txt", + "subfolder1/subfolder2/subfolder3/zipEntry3.txt", + "subfolder1/subfolder2/zipEntry1.txt" + ].sort(); + + expect(zip1Entries).to.deep.equal(expected); + done(); + }); + }); + }); + + it("zip.addLocalFolderAsync2({localPath, namefix}, callback)", (done) => { + const zip = new Zip(); + const namefix = (str) => str.toLowerCase(); + zip.addLocalFolderAsync2({ localPath: destination, namefix }, (error) => { + if (error) done(false); + + zip.toBuffer(function () { + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = [ + "subfolder1/", + "subfolder1/subfolder2/", + "subfolder1/subfolder2/subfolder3/", + "subfolder1/subfolder2/subfolder3/subfolder4/", + "subfolder1/subfolder2/subfolder3/zipentry2.txt", + "subfolder1/subfolder2/subfolder3/zipentry3.txt", + "subfolder1/subfolder2/zipentry1.txt" + ].sort(); + + expect(zip1Entries).to.deep.equal(expected); + done(); + }); + }); + }); + + it("zip.addLocalFolderAsync2({localPath, namefix}, callback)", (done) => { + const zip = new Zip(); + genFiles([{ name: "subfolder1/æble.txt", content: "apple" }], destination); + + zip.addLocalFolderAsync2({ localPath: destination, namefix: "latin1" }, (error) => { + if (error) done(false); + + zip.toBuffer(function () { + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = [ + "subfolder1/", + "subfolder1/ble.txt", + "subfolder1/subfolder2/", + "subfolder1/subfolder2/subfolder3/", + "subfolder1/subfolder2/subfolder3/subfolder4/", + "subfolder1/subfolder2/subfolder3/zipEntry2.txt", + "subfolder1/subfolder2/subfolder3/zipEntry3.txt", + "subfolder1/subfolder2/zipEntry1.txt" + ].sort(); + + expect(zip1Entries).to.deep.equal(expected); + done(); + }); + }); + }); + + it("zip.addLocalFolderAsync2({localPath, zipPath}, callback)", (done) => { + const zip = new Zip(); + zip.addLocalFolderAsync2({ localPath: destination, zipPath: "parent" }, (error) => { + if (error) done(false); + + zip.toBuffer(function () { + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = [ + "parent/subfolder1/", + "parent/subfolder1/subfolder2/", + "parent/subfolder1/subfolder2/subfolder3/", + "parent/subfolder1/subfolder2/subfolder3/subfolder4/", + "parent/subfolder1/subfolder2/subfolder3/zipEntry2.txt", + "parent/subfolder1/subfolder2/subfolder3/zipEntry3.txt", + "parent/subfolder1/subfolder2/zipEntry1.txt" + ].sort(); + + expect(zip1Entries).to.deep.equal(expected); + done(); + }); + }); + }); + + it("zip.addLocalFolderAsync2({localPath, filter}, callback)", (done) => { + const zip = new Zip(); + const filter = /zipEntry[23]\.txt/; + zip.addLocalFolderAsync2({ localPath: destination, filter }, (error) => { + if (error) done(false); + + zip.toBuffer(function () { + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = ["subfolder1/subfolder2/subfolder3/zipEntry2.txt", "subfolder1/subfolder2/subfolder3/zipEntry3.txt"].sort(); + + expect(zip1Entries).to.deep.equal(expected); + done(); + }); + }); + }); + + it("zip.addLocalFolderAsync2({localPath, filter}, callback)", (done) => { + const zip = new Zip(); + const filter = (str) => str.slice(-1) === "/"; + zip.addLocalFolderAsync2({ localPath: destination, filter }, (error) => { + if (error) done(false); + + zip.toBuffer(function () { + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = ["subfolder1/", "subfolder1/subfolder2/", "subfolder1/subfolder2/subfolder3/", "subfolder1/subfolder2/subfolder3/subfolder4/"].sort(); + + expect(zip1Entries).to.deep.equal(expected); + done(); + }); + }); + }); + }); + + describe(".addLocalFolderPromise() - promise", () => { + beforeEach(() => { + genFiles(testFileFolderList, destination); + }); + + it("zip.addLocalFolderPromise(destination)", async function () { + const zip = new Zip(); + const zip1 = await zip.addLocalFolderPromise(destination); + + zip1.toBuffer(); + const zip1Entries = zip1.getEntries().map((e) => e.entryName); + + const expected = [ + "subfolder1/", + "subfolder1/subfolder2/", + "subfolder1/subfolder2/subfolder3/", + "subfolder1/subfolder2/zipEntry1.txt", + "subfolder1/subfolder2/subfolder3/subfolder4/", + "subfolder1/subfolder2/subfolder3/zipEntry2.txt", + "subfolder1/subfolder2/subfolder3/zipEntry3.txt" + ].sort(); + + expect(zip1Entries).to.deep.equal(expected.sort()); + }); + + it("zip.addLocalFolderPromise(destination, {namefix})", async function () { + const zip = new Zip(); + const namefix = (str) => str.toLowerCase(); + const zip1 = await zip.addLocalFolderPromise(destination, { namefix }); + + zip1.toBuffer(); + const zip1Entries = zip1.getEntries().map((e) => e.entryName); + + const expected = [ + "subfolder1/", + "subfolder1/subfolder2/", + "subfolder1/subfolder2/subfolder3/", + "subfolder1/subfolder2/subfolder3/subfolder4/", + "subfolder1/subfolder2/subfolder3/zipentry2.txt", + "subfolder1/subfolder2/subfolder3/zipentry3.txt", + "subfolder1/subfolder2/zipentry1.txt" + ].sort(); + + expect(zip1Entries).to.deep.equal(expected.sort()); + }); + + it("zip.addLocalFolderPromise(destination, {zipPath})", async function () { + const zip = new Zip(); + await zip.addLocalFolderPromise(destination, { zipPath: "parent" }); + const zip1Entries = zip.getEntries().map((e) => e.entryName); + + const expected = [ + "parent/subfolder1/", + "parent/subfolder1/subfolder2/", + "parent/subfolder1/subfolder2/subfolder3/", + "parent/subfolder1/subfolder2/zipEntry1.txt", + "parent/subfolder1/subfolder2/subfolder3/subfolder4/", + "parent/subfolder1/subfolder2/subfolder3/zipEntry2.txt", + "parent/subfolder1/subfolder2/subfolder3/zipEntry3.txt" + ]; + + expect(zip1Entries.sort()).to.deep.equal(expected.sort()); + }); + }); +}); + +function walk(dir) { + let results = []; + const list = fs.readdirSync(dir); + list.forEach(function (file) { + file = dir + "/" + file; + const stat = fs.statSync(file); + if (stat && stat.isDirectory()) { + /* Recurse into a subdirectory */ + results = results.concat(walk(file)); + } else { + /* Is a file */ + results.push(pth.normalize(file)); + } + }); + return results; +} + +function genFiles(list, location) { + const utils = new Utils({ fs }); + + for (const el of list) { + const path = pth.resolve(location, el.name); + if (el.name.slice(-1) === "/") { + utils.makeDir(path); + } else { + utils.makeDir(pth.dirname(path)); + fs.writeFileSync(path, el.content, "utf8"); + } + } +} diff --git a/test/mocha.js b/test/mocha.js index 9b816b1..9319061 100644 --- a/test/mocha.js +++ b/test/mocha.js @@ -11,131 +11,6 @@ describe("adm-zip", () => { // clean up folder content afterEach((done) => rimraf(destination, done)); - it("zip.extractAllTo()", () => { - const zip = new Zip("./test/assets/ultra.zip"); - zip.extractAllTo(destination); - const files = walk(destination); - - expect(files.sort()).to.deep.equal( - [ - pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), - pth.normalize("./test/xxx/attributes_test/blank file.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), - pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") - ].sort() - ); - }); - - it("zip.extractAllToAsync(destination)", (done) => { - const zip = new Zip("./test/assets/ultra.zip"); - zip.extractAllToAsync(destination, (error) => { - const files = walk(destination); - expect(files.sort()).to.deep.equal( - [ - pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), - pth.normalize("./test/xxx/attributes_test/blank file.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), - pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") - ].sort() - ); - done(); - }); - }); - - it("zip.extractAllToAsync(destination) [Promise]", function () { - const zip = new Zip("./test/assets/ultra.zip"); - // note the return - return zip.extractAllToAsync(destination).then(function (data) { - const files = walk(destination); - expect(files.sort()).to.deep.equal( - [ - pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), - pth.normalize("./test/xxx/attributes_test/blank file.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), - pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") - ].sort() - ); - }); // no catch, it'll figure it out since the promise is rejected - }); - - it("zip.extractAllToAsync(destination, false, false, callback)", (done) => { - const zip = new Zip("./test/assets/ultra.zip"); - zip.extractAllToAsync(destination, false, false, (error) => { - const files = walk(destination); - expect(files.sort()).to.deep.equal( - [ - pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), - pth.normalize("./test/xxx/attributes_test/blank file.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), - pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") - ].sort() - ); - done(); - }); - }); - - it("zip.extractAllToAsync(destination, false, false) [Promise]", function () { - const zip = new Zip("./test/assets/ultra.zip"); - // note the return - return zip.extractAllToAsync(destination, false, false).then(function (data) { - const files = walk(destination); - expect(files.sort()).to.deep.equal( - [ - pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), - pth.normalize("./test/xxx/attributes_test/blank file.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), - pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") - ].sort() - ); - }); // no catch, it'll figure it out since the promise is rejected - }); - - it("zip.extractAllToAsync(destination, false, callback)", (done) => { - const zip = new Zip("./test/assets/ultra.zip"); - zip.extractAllToAsync(destination, false, (error) => { - const files = walk(destination); - expect(files.sort()).to.deep.equal( - [ - pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), - pth.normalize("./test/xxx/attributes_test/blank file.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), - pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") - ].sort() - ); - done(); - }); - }); - - it("zip.extractAllToAsync(destination, false) [Promise]", () => { - const zip = new Zip("./test/assets/ultra.zip"); - // note the return - return zip.extractAllToAsync(destination, false).then(function (data) { - const files = walk(destination); - expect(files.sort()).to.deep.equal( - [ - pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), - pth.normalize("./test/xxx/attributes_test/blank file.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), - pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") - ].sort() - ); - }); // no catch, it'll figure it out since the promise is rejected - }); - it("zip pathTraversal", () => { const target = pth.join(destination, "test"); const zip = new Zip(); @@ -167,42 +42,6 @@ describe("adm-zip", () => { expect(zip2Entries).to.deep.equal(["dir11/", "dir11/dir21/", "dir11/dir22/", "dir11/dir22/test.txt", "dir12/", "dir12/dir23/", "dir13/", "dir13/dir24/"]); }); - it("zip.extractEntryTo(entry, destination, false, true)", () => { - const zip = new Zip("./test/assets/ultra.zip"); - var zipEntries = zip.getEntries(); - zipEntries.forEach((e) => zip.extractEntryTo(e, destination, false, true)); - - const files = walk(destination); - expect(files.sort()).to.deep.equal( - [ - pth.normalize("./test/xxx/blank file.txt"), - pth.normalize("./test/xxx/hidden.txt"), - pth.normalize("./test/xxx/hidden_readonly.txt"), - pth.normalize("./test/xxx/New Text Document.txt"), - pth.normalize("./test/xxx/readonly.txt"), - pth.normalize("./test/xxx/somefile.txt") - ].sort() - ); - }); - - it("zip.extractEntryTo(entry, destination, true, true)", () => { - const zip = new Zip("./test/assets/ultra.zip"); - var zipEntries = zip.getEntries(); - zipEntries.forEach((e) => zip.extractEntryTo(e, destination, true, true)); - - const files = walk(destination); - expect(files.sort()).to.deep.equal( - [ - pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"), - pth.normalize("./test/xxx/attributes_test/blank file.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"), - pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"), - pth.normalize("./test/xxx/utes_test/New folder/somefile.txt") - ].sort() - ); - }); - it("passes issue-237-Twizzeld test case", () => { const zip = new Zip("./test/assets/issue-237-Twizzeld.zip"); const zipEntries = zip.getEntries(); From 719817618656a8154c2e68ebc85e51e206720b2d Mon Sep 17 00:00:00 2001 From: 5saviahv <5saviahv@users.noreply.github.com> Date: Sun, 9 Jun 2024 00:25:52 +0300 Subject: [PATCH 5/5] update tests --- test/methods/zipcrypto.test.js | 252 +++++++++++++++++---------------- test/utils.test.js | 160 +++++++++++---------- 2 files changed, 210 insertions(+), 202 deletions(-) diff --git a/test/methods/zipcrypto.test.js b/test/methods/zipcrypto.test.js index 2b96494..994c349 100644 --- a/test/methods/zipcrypto.test.js +++ b/test/methods/zipcrypto.test.js @@ -6,134 +6,136 @@ const { crc32 } = require("../../util/utils"); // node crypto const { createHash } = require("crypto"); -describe("method - zipcrypto decrypt", () => { - const source = { - crc: 0xd87f7e0c, - // 16 byte buffer as test source - data: Buffer.from("D1Q5///EbpBY6rHIZXvd3A==", "base64"), - // just data integrity check - md5: "wYHjota6dQNazueWO9/uDg==", - pwdok: "secret", - pwdbad: "Secret", - flagsencrypted: 0x01, - flagsinfozipencrypted: 0x09, - timeHighByte: 0xd8, - // result - result: Buffer.from("test", "ascii") - }; - - // test invalid input data - it("handles invalid data field values / types", () => { - for (const data of [undefined, null, "str", true, false, 6, Buffer.alloc(4)]) { - const result = decrypt(data, { crc: source.crc }, source.pwdok); - expect(result).to.have.lengthOf(0); - } +describe("method - zipcrypto", () => { + describe("zipcrypto decrypt", () => { + const source = { + crc: 0xd87f7e0c, + // 16 byte buffer as test source + data: Buffer.from("D1Q5///EbpBY6rHIZXvd3A==", "base64"), + // just data integrity check + md5: "wYHjota6dQNazueWO9/uDg==", + pwdok: "secret", + pwdbad: "Secret", + flagsencrypted: 0x01, + flagsinfozipencrypted: 0x09, + timeHighByte: 0xd8, + // result + result: Buffer.from("test", "ascii") + }; + + // test invalid input data + it("handles invalid data field values / types", () => { + for (const data of [undefined, null, "str", true, false, 6, Buffer.alloc(4)]) { + const result = decrypt(data, { crc: source.crc }, source.pwdok); + expect(result).to.have.lengthOf(0); + } + }); + + // test is data intact + it("is test data valid", () => { + // source data + const md5sum = createHash("md5"); + md5sum.update(source.data); + expect(md5sum.digest("base64")).to.equal(source.md5); + // result data + expect(crc32(source.result)).to.equal(source.crc); + }); + + // is error thrown if invalid password was provided + it("should throw if invalid password is provided", () => { + expect(function badpassword() { + decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdbad); + }).to.throw(); + + expect(function okpassword() { + decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdok); + }).to.not.throw(); + }); + + // is error thrown if invalid password was provided + it("should throw if invalid password is provided for Info-Zip bit 3 flag", () => { + expect(function badpassword() { + decrypt(source.data, { crc: source.crc, flags: source.flagsinfozipencrypted, timeHighByte: source.timeHighByte }, source.pwdbad); + }).to.throw(); + + expect(function okpassword() { + decrypt(source.data, { crc: source.crc, flags: source.flagsinfozipencrypted, timeHighByte: source.timeHighByte }, source.pwdok); + }).to.not.throw(); + }); + + // test decryption with both password types + it("test decrypted data with password", () => { + // test password, string + const result1 = decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdok); + expect(result1.compare(source.result)).to.equal(0); + + // test password, buffer + const result2 = decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, Buffer.from(source.pwdok, "ascii")); + expect(result2.compare(source.result)).to.equal(0); + }); }); - // test is data intact - it("is test data valid", () => { - // source data - const md5sum = createHash("md5"); - md5sum.update(source.data); - expect(md5sum.digest("base64")).to.equal(source.md5); - // result data - expect(crc32(source.result)).to.equal(source.crc); - }); - - // is error thrown if invalid password was provided - it("should throw if invalid password is provided", () => { - expect(function badpassword() { - decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdbad); - }).to.throw(); - - expect(function okpassword() { - decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdok); - }).to.not.throw(); - }); - - // is error thrown if invalid password was provided - it("should throw if invalid password is provided for Info-Zip bit 3 flag", () => { - expect(function badpassword() { - decrypt(source.data, { crc: source.crc, flags: source.flagsinfozipencrypted, timeHighByte: source.timeHighByte }, source.pwdbad); - }).to.throw(); - - expect(function okpassword() { - decrypt(source.data, { crc: source.crc, flags: source.flagsinfozipencrypted, timeHighByte: source.timeHighByte }, source.pwdok); - }).to.not.throw(); - }); - - // test decryption with both password types - it("test decrypted data with password", () => { - // test password, string - const result1 = decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, source.pwdok); - expect(result1.compare(source.result)).to.equal(0); - - // test password, buffer - const result2 = decrypt(source.data, { crc: source.crc, flags: source.flagsencrypted }, Buffer.from(source.pwdok, "ascii")); - expect(result2.compare(source.result)).to.equal(0); - }); -}); - -describe("method - zipcrypto encrypt", () => { - const source = { - crc: 0xd87f7e0c, - // data - data_str: "test", - data_buffer: Buffer.from("test", "ascii"), - salt: Buffer.from("xx+OYQ1Pkvo0ztPY", "base64"), - // 16 byte buffer as test source - data: Buffer.from("D1Q5///EbpBY6rHIZXvd3A==", "base64"), - // just data integrity check - pwdok: "secret", - // result - result: Buffer.from("D1Q5///EbpBY6rHIZXvd3A==", "base64") - }; - - // test binary results with known salt - it("test binary results with known salt", () => { - const head = { crc: source.crc }; - // inject known salt - _salter(source.salt); - const result = encrypt(source.data_str, head, source.pwdok, false); - expect(result.compare(source.result)).to.equal(0); - // restore salting - _salter(); - }); - - // test decryption with both password types - it("test encryption and decrytion with node random salt", () => { - const head = { crc: source.crc }; - _salter("node"); - // test password, string - const data_buf = Buffer.from(source.data_str); - const result1 = encrypt(source.data_str, head, source.pwdok, false); - const result2 = decrypt(result1, head, source.pwdok); - expect(result2.compare(data_buf)).to.equal(0); - _salter(); - }); - - // test decryption with both password types - it("test encryption and decrytion with known source data", () => { - const head = { crc: source.crc }; - // test password, string - const data_buf = Buffer.from(source.data_str); - const result1 = encrypt(source.data_str, head, source.pwdok, false); - const result2 = decrypt(result1, head, source.pwdok); - expect(result2.compare(data_buf)).to.equal(0); - }); - - // test how encrytion will handle some random data - it("test encrypting and decryting with some javascript objects", () => { - const tests = [true, null, false, undefined, {}, [], 747, new Date(), [{}]]; - const head = {}; - - for (const test of tests) { - const data_buf = test == null ? Buffer.alloc(0) : Buffer.from(test.toString()); - head.crc = crc32(data_buf); - - const result1 = encrypt(test, head, source.pwdok, false); + describe("zipcrypto encrypt", () => { + const source = { + crc: 0xd87f7e0c, + // data + data_str: "test", + data_buffer: Buffer.from("test", "ascii"), + salt: Buffer.from("xx+OYQ1Pkvo0ztPY", "base64"), + // 16 byte buffer as test source + data: Buffer.from("D1Q5///EbpBY6rHIZXvd3A==", "base64"), + // just data integrity check + pwdok: "secret", + // result + result: Buffer.from("D1Q5///EbpBY6rHIZXvd3A==", "base64") + }; + + // test binary results with known salt + it("test binary results with known salt", () => { + const head = { crc: source.crc }; + // inject known salt + _salter(source.salt); + const result = encrypt(source.data_str, head, source.pwdok, false); + expect(result.compare(source.result)).to.equal(0); + // restore salting + _salter(); + }); + + // test decryption with both password types + it("test encryption and decrytion with node random salt", () => { + const head = { crc: source.crc }; + _salter("node"); + // test password, string + const data_buf = Buffer.from(source.data_str); + const result1 = encrypt(source.data_str, head, source.pwdok, false); + const result2 = decrypt(result1, head, source.pwdok); + expect(result2.compare(data_buf)).to.equal(0); + _salter(); + }); + + // test decryption with both password types + it("test encryption and decrytion with known source data", () => { + const head = { crc: source.crc }; + // test password, string + const data_buf = Buffer.from(source.data_str); + const result1 = encrypt(source.data_str, head, source.pwdok, false); const result2 = decrypt(result1, head, source.pwdok); expect(result2.compare(data_buf)).to.equal(0); - } + }); + + // test how encrytion will handle some random data + it("test encrypting and decryting with some javascript objects", () => { + const tests = [true, null, false, undefined, {}, [], 747, new Date(), [{}]]; + const head = {}; + + for (const test of tests) { + const data_buf = test == null ? Buffer.alloc(0) : Buffer.from(test.toString()); + head.crc = crc32(data_buf); + + const result1 = encrypt(test, head, source.pwdok, false); + const result2 = decrypt(result1, head, source.pwdok); + expect(result2.compare(data_buf)).to.equal(0); + } + }); }); }); diff --git a/test/utils.test.js b/test/utils.test.js index 5944d5b..d195e16 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -3,90 +3,96 @@ const { expect } = require("chai"); const { crc32, canonical, sanitize } = require("../util/utils"); const pth = require("path"); -describe("utils - crc32 function", () => { - // tests how crc32 function handles strings as input - it("handle strings", () => { - const tests = [ - // basic latin - { crc: 0x00000000, data: "" }, - { crc: 0xe8b7be43, data: "a" }, - { crc: 0x352441c2, data: "abc" }, - { crc: 0xcbf43926, data: "123456789" }, - { crc: 0x20159d7f, data: "message digest" }, - { crc: 0x4c2750bd, data: "abcdefghijklmnopqrstuvwxyz" }, - { crc: 0x1fc2e6d2, data: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, - { crc: 0xf8c05f58, data: "1234567890123456789012345678901234567890123456789" }, - { crc: 0x1f61e4e0, data: "FFFFFFFFFFFFFFFFFFFFFFFFFFF" }, - // Unicode - { crc: 0x70b5f183, data: "ä" }, - { crc: 0x414fa339, data: "The quick brown fox jumps over the lazy dog" }, - // fox jump in russian - { crc: 0x7d67cd7a, data: "Быстрая коричневая лиса прыгает через ленивую собаку" }, - // fox jump in german - { crc: 0x8c3db82b, data: "Der schnelle Braunfuchs springt über den faulen Hund" }, - // fox jump in arabic - { crc: 0x6d8c0241, data: "الثعلب البني السريع يقفز فوق الكلب الكسول" }, - // fox jump in korean - { crc: 0x13a25011, data: "빠른 갈색 여우가 게으른 개를 뛰어 넘습니다." } - ]; +describe("utils", () => { + describe("crc32 function", () => { + // tests how crc32 function handles strings as input + it("handle strings", () => { + const tests = [ + // basic latin + { crc: 0x00000000, data: "" }, + { crc: 0xe8b7be43, data: "a" }, + { crc: 0x352441c2, data: "abc" }, + { crc: 0xcbf43926, data: "123456789" }, + { crc: 0x20159d7f, data: "message digest" }, + { crc: 0x4c2750bd, data: "abcdefghijklmnopqrstuvwxyz" }, + { crc: 0x1fc2e6d2, data: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, + { crc: 0xf8c05f58, data: "1234567890123456789012345678901234567890123456789" }, + { crc: 0x1f61e4e0, data: "FFFFFFFFFFFFFFFFFFFFFFFFFFF" }, + // Unicode + { crc: 0x70b5f183, data: "ä" }, + { crc: 0x414fa339, data: "The quick brown fox jumps over the lazy dog" }, + // fox jump in russian + { crc: 0x7d67cd7a, data: "Быстрая коричневая лиса прыгает через ленивую собаку" }, + // fox jump in german + { crc: 0x8c3db82b, data: "Der schnelle Braunfuchs springt über den faulen Hund" }, + // fox jump in arabic + { crc: 0x6d8c0241, data: "الثعلب البني السريع يقفز فوق الكلب الكسول" }, + // fox jump in korean + { crc: 0x13a25011, data: "빠른 갈색 여우가 게으른 개를 뛰어 넘습니다." } + ]; - for (let test of tests) { - expect(crc32(test.data)).to.equal(test.crc); - } + for (let test of tests) { + expect(crc32(test.data)).to.equal(test.crc); + } + }); }); -}); -describe("utils - sanitizing functions :", () => { - // tests how sanitize works - it("function sanitize()", () => { - const tests = [ - // basic latin - { prefix: "", file: "", result: "" }, - { prefix: "folder", file: "file", result: "folder/file" }, - { prefix: "folder", file: "../file", result: "folder/file" }, - { prefix: "folder", file: "../../../file", result: "folder/file" }, - { prefix: "folder", file: "./../file", result: "folder/file" }, - { prefix: "test/folder/subfolder", file: "../../file", result: "test/folder/subfolder/file" }, - { prefix: "test/folder/subfolder", file: "../../file1/../file2", result: "test/folder/subfolder/file2" }, - // no prefixed (currently allows change folder) - { prefix: "", file: "../../file1/../file2", result: "file2" }, - { prefix: "", file: "../subfolder/file2", result: "subfolder/file2" }, - { prefix: "", file: "../subfolder2/file2", result: "subfolder2/file2" }, - { prefix: "", file: "../subfolder/file2", result: "subfolder/file2" }, - { prefix: "", file: "../../subfolder2/file2", result: "subfolder2/file2" } - ]; + describe("sanitizing functions :", () => { + // tests how sanitize works + it("function sanitize()", () => { + const tests = [ + // basic latin + { prefix: "", file: "", result: "" }, + { prefix: "folder", file: "file", result: "folder/file" }, + { prefix: "folder", file: "../file", result: "folder/file" }, + { prefix: "folder", file: "../../../file", result: "folder/file" }, + { prefix: "folder", file: "./../file", result: "folder/file" }, + { prefix: "test/folder/subfolder", file: "../../file", result: "test/folder/subfolder/file" }, + { prefix: "test/folder/subfolder", file: "../../file1/../file2", result: "test/folder/subfolder/file2" }, + // no prefixed (currently allows change folder) + { prefix: "", file: "../../file1/../file2", result: "file2" }, + { prefix: "", file: "../subfolder/file2", result: "subfolder/file2" }, + { prefix: "", file: "../subfolder2/file2", result: "subfolder2/file2" }, + { prefix: "", file: "../subfolder/file2", result: "subfolder/file2" }, + { prefix: "", file: "../../subfolder2/file2", result: "subfolder2/file2" } + ]; - const curfolder = pth.resolve("."); - // console.log("\n"); - for (let test of tests) { - // path.normalize in win32 will convert "/" to native "\" format + const curfolder = pth.resolve("."); + // console.log("\n"); + for (let test of tests) { + // path.normalize in win32 will convert "/" to native "\" format - const out = sanitize(pth.normalize(test.prefix || ""), test.file); - const res = pth.join(curfolder, pth.normalize(test.result)); + const out = sanitize(pth.normalize(test.prefix || ""), test.file); + const res = pth.join(curfolder, pth.normalize(test.result)); - expect(out).to.equal(res); - } - }); + expect(out).to.equal(res); + } + }); + + it("function canonical()", () => { + const tests = [ + // no name + { file: "", result: "" }, + // file has name + { file: "file", result: "file" }, + { file: "../file", result: "file" }, + { file: "../../../file", result: "file" }, + { file: "./../file", result: "file" }, + { file: "../../file", result: "file" }, + { file: "../../file1/../file2", result: "file2" }, + { file: "../subfolder/file2", result: "subfolder/file2" }, + { file: "../subfolder2/file2", result: "subfolder2/file2" }, + { file: "../subfolder/file2", result: "subfolder/file2" }, + { file: "../../subfolder2/file2", result: "subfolder2/file2" } + ]; - it("function canonical()", () => { - const tests = [ - // no name - { file: "", result: "" }, - // file has name - { file: "file", result: "file" }, - { file: "../file", result: "file" }, - { file: "../../../file", result: "file" }, - { file: "./../file", result: "file" }, - { file: "../../file", result: "file" }, - { file: "../../file1/../file2", result: "file2" }, - { file: "../subfolder/file2", result: "subfolder/file2" }, - { file: "../subfolder2/file2", result: "subfolder2/file2" }, - { file: "../subfolder/file2", result: "subfolder/file2" }, - { file: "../../subfolder2/file2", result: "subfolder2/file2" } - ]; + for (const { file, result } of Array.from(tests)) { + tests.push({ result, file: file.split("/").join("\\") }); + } - for (let test of tests) { - expect(canonical(test.file)).to.equal(test.result); - } + for (let test of tests) { + expect(canonical(test.file)).to.equal(test.result); + } + }); }); });