From 717117c484e871b7cb4f5f258087147201293c7d Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Sat, 8 Jul 2023 11:07:11 +0200 Subject: [PATCH 1/7] Add more passwords --- __test__/sevenZip.test.ts | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/__test__/sevenZip.test.ts b/__test__/sevenZip.test.ts index 15da10b..d98428d 100644 --- a/__test__/sevenZip.test.ts +++ b/__test__/sevenZip.test.ts @@ -85,20 +85,26 @@ describe("Test sevenZip", function () { const fileName = "file.txt"; const file = path.join(testBaseDir, fileName); const zip = path.join(testBaseDir, "file.7z"); - const password = "secret"; - fs.writeFileSync(file, "file"); - expect(fs.existsSync(file)).toBe(true); - expect(fs.existsSync(zip)).toBe(false); - const result = await sevenZip.add(zip, file, password); - expect(result).toBe(true); - expect(fs.existsSync(zip)).toBe(true); - - expect(await sevenZip.passwordProtected(zip)).toBe(true); - - const sevenZipList = await sevenZip.list(zip, password); - expect(sevenZipList.length).toBe(1); - expect(sevenZipList[0].file).toBe(fileName); + const passwords = ["scret", "bla!", 'VCe`,=/P<_+.7]~;Ys("']; + + for (const password of passwords) { + fs.writeFileSync(file, "file"); + expect(fs.existsSync(file)).toBe(true); + expect(fs.existsSync(zip)).toBe(false); + const result = await sevenZip.add(zip, file, password, { + method: ["x0"], + }); + expect(result).toBe(true); + expect(fs.existsSync(zip)).toBe(true); + expect(await sevenZip.passwordProtected(zip)).toBe(true); + const sevenZipList = await sevenZip.list(zip, password); + + expect(sevenZipList.length).toBe(1); + expect(sevenZipList[0].file).toBe(fileName); + + fs.removeSync(zip); + } }); }); }); From a4af0269d493375ff0ce0c48e4013c961ee41bd2 Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Sat, 8 Jul 2023 12:17:24 +0200 Subject: [PATCH 2/7] Throw error when password contains " --- __test__/sevenZip.test.ts | 32 ++++++++++++++++++++++---------- src/sevenZip.ts | 4 ++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/__test__/sevenZip.test.ts b/__test__/sevenZip.test.ts index d98428d..cda2de5 100644 --- a/__test__/sevenZip.test.ts +++ b/__test__/sevenZip.test.ts @@ -92,17 +92,29 @@ describe("Test sevenZip", function () { fs.writeFileSync(file, "file"); expect(fs.existsSync(file)).toBe(true); expect(fs.existsSync(zip)).toBe(false); - const result = await sevenZip.add(zip, file, password, { - method: ["x0"], - }); - expect(result).toBe(true); - expect(fs.existsSync(zip)).toBe(true); - expect(await sevenZip.passwordProtected(zip)).toBe(true); - const sevenZipList = await sevenZip.list(zip, password); - - expect(sevenZipList.length).toBe(1); - expect(sevenZipList[0].file).toBe(fileName); + if (password.indexOf('"') >= 0) { + let errorThrown = null; + try { + errorThrown = false; + await sevenZip.add(zip, file, password, { method: ["x0"] }); + } catch { + errorThrown = true; + } + console.log(">>>" + errorThrown); + expect(errorThrown).toBe(true); + } else { + const result = await sevenZip.add(zip, file, password, { + method: ["x0"], + }); + expect(result).toBe(true); + expect(fs.existsSync(zip)).toBe(true); + expect(await sevenZip.passwordProtected(zip)).toBe(true); + const sevenZipList = await sevenZip.list(zip, password); + + expect(sevenZipList.length).toBe(1); + expect(sevenZipList[0].file).toBe(fileName); + } fs.removeSync(zip); } }); diff --git a/src/sevenZip.ts b/src/sevenZip.ts index c2d86c8..ef2a527 100644 --- a/src/sevenZip.ts +++ b/src/sevenZip.ts @@ -35,6 +35,10 @@ export namespace sevenZip { if (!_7zOptions.method) { _7zOptions.method = []; } + if (password.indexOf('"') >= 0) { + throw 'Password contains " (double quotes) https://github.com/quentinrossetti/node-7z/issues/132'; + } + _7zOptions.password = password; return _7zOptions; } From 5734752a199e337ee89a6d1e1cdeb7b52086bc07 Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Sat, 8 Jul 2023 13:10:53 +0200 Subject: [PATCH 3/7] Remove debug output --- __test__/sevenZip.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/__test__/sevenZip.test.ts b/__test__/sevenZip.test.ts index cda2de5..5911447 100644 --- a/__test__/sevenZip.test.ts +++ b/__test__/sevenZip.test.ts @@ -101,7 +101,6 @@ describe("Test sevenZip", function () { } catch { errorThrown = true; } - console.log(">>>" + errorThrown); expect(errorThrown).toBe(true); } else { const result = await sevenZip.add(zip, file, password, { From 589473b4890e672c055880835c2aee8c3aa8e97c Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Sat, 8 Jul 2023 13:15:38 +0200 Subject: [PATCH 4/7] Add workarround for node-7z bug with " --- __test__/backup.test.ts | 1 + __test__/pw.test.ts | 81 +++++++++++++++++++++++++++++++++++++++++ src/Backup.ts | 27 ++++++++++++-- src/locales/de_DE.json | 3 +- src/locales/en_US.json | 3 +- 5 files changed, 110 insertions(+), 5 deletions(-) diff --git a/__test__/backup.test.ts b/__test__/backup.test.ts index 76b9c0f..0034874 100644 --- a/__test__/backup.test.ts +++ b/__test__/backup.test.ts @@ -4,6 +4,7 @@ import * as path from "path"; import { when } from "jest-when"; import { sevenZip } from "../src/sevenZip"; import joplin from "api"; +import { I18n } from "i18n"; function getTestPaths(): any { const testPath: any = {}; diff --git a/__test__/pw.test.ts b/__test__/pw.test.ts index 30317b9..e132106 100644 --- a/__test__/pw.test.ts +++ b/__test__/pw.test.ts @@ -1,12 +1,40 @@ import { Backup } from "../src/Backup"; import joplin from "api"; import { when } from "jest-when"; +import { I18n } from "i18n"; let backup = null; +let spyOnLogVerbose = null; +let spyOnLogInfo = null; +let spyOnLogWarn = null; +let spyOnLogError = null; +let spyOnShowError = null; + describe("Password", function () { beforeEach(async () => { backup = new Backup() as any; + + spyOnLogVerbose = jest + .spyOn(backup.log, "verbose") + .mockImplementation(() => {}); + spyOnLogInfo = jest.spyOn(backup.log, "info").mockImplementation(() => {}); + spyOnLogWarn = jest.spyOn(backup.log, "warn").mockImplementation(() => {}); + spyOnLogError = jest + .spyOn(backup.log, "error") + .mockImplementation(() => {}); + + spyOnShowError = jest + .spyOn(backup, "showError") + .mockImplementation(() => {}); + }); + + afterEach(async () => { + spyOnLogVerbose.mockReset(); + spyOnLogInfo.mockReset(); + spyOnLogWarn.mockReset(); + spyOnLogError.mockReset(); + spyOnShowError.mockReset(); }); it(`Check`, async () => { @@ -75,8 +103,61 @@ describe("Password", function () { expect(await backup.checkPassword()).toBe(testCase.expected); await backup.enablePassword(); + + if (testCase.expected == 1) { + expect(backup.password).toBe(testCase.password); + } expect(spyOnsSettingsSetValue).toBeCalledTimes(testCase.called); + expect(backup.log.error).toHaveBeenCalledTimes(0); + expect(backup.log.warn).toHaveBeenCalledTimes(0); + spyOnsSettingsSetValue.mockReset(); + } + }); + + it(`Check node-7z bug`, async () => { + const spyOnsSettingsValue = jest.spyOn(joplin.settings, "value"); + const spyOnsSettingsSetValue = jest.spyOn(joplin.settings, "setValue"); + jest.spyOn(backup, "getTranslation").mockImplementation(() => {}); + const spyOnShowMsg = jest + .spyOn(backup, "showMsg") + .mockImplementation(() => {}); + + const testCases = [ + { + password: "1password", + fail: false, + }, + { + password: '2pass"word', + fail: true, + }, + ]; + + for (const testCase of testCases) { + when(spyOnsSettingsValue) + .mockImplementation(() => Promise.resolve("no mockImplementation")) + .calledWith("usePassword") + .mockImplementation(() => Promise.resolve(true)) + .calledWith("password") + .mockImplementation(() => Promise.resolve(testCase.password)) + .calledWith("passwordRepeat") + .mockImplementation(() => Promise.resolve(testCase.password)); + + await backup.enablePassword(); + + if (testCase.fail == false) { + expect(backup.password).toBe(testCase.password); + expect(backup.log.error).toHaveBeenCalledTimes(0); + expect(spyOnShowMsg).toHaveBeenCalledTimes(0); + } else { + expect(backup.password).toBe(null); + expect(backup.log.error).toHaveBeenCalledTimes(1); + expect(spyOnShowMsg).toHaveBeenCalledTimes(1); + } + + expect(backup.log.warn).toHaveBeenCalledTimes(0); spyOnsSettingsSetValue.mockReset(); + spyOnShowMsg.mockReset(); } }); }); diff --git a/src/Backup.ts b/src/Backup.ts index b47114b..7f3fc37 100644 --- a/src/Backup.ts +++ b/src/Backup.ts @@ -156,11 +156,33 @@ class Backup { await Settings.register(); } + // For mock ups + private async getTranslation(key: string): Promise { + return i18n.__(key); + } + private async enablePassword() { const usePassword = await joplin.settings.value("usePassword"); if (usePassword === true && (await this.checkPassword()) === 1) { - this.passwordEnabled = true; - this.password = await joplin.settings.value("password"); + const pw = await joplin.settings.value("password"); + + // Check for node-7z bug with double quotes + // https://github.com/JackGruber/joplin-plugin-backup/issues/53 + // https://github.com/quentinrossetti/node-7z/issues/132 + if (pw.indexOf('"') >= 0) { + this.log.error( + 'enablePassword: Password contains " (double quotes), disable password' + ); + this.passwordEnabled = false; + this.password = null; + + await this.showMsg( + await this.getTranslation("error.passwordDoubleQuotes") + ); + } else { + this.passwordEnabled = true; + this.password = pw; + } } else { this.passwordEnabled = false; this.password = null; @@ -324,7 +346,6 @@ class Backup { await joplin.views.dialogs.setButtons(this.msgDialog, [{ id: "ok" }]); await joplin.views.dialogs.setHtml(this.msgDialog, html.join("\n")); await joplin.views.dialogs.open(this.msgDialog); - this.backupStartTime = null; } private async showError(msg: string, title: string = null) { diff --git a/src/locales/de_DE.json b/src/locales/de_DE.json index f38403a..16e54d1 100644 --- a/src/locales/de_DE.json +++ b/src/locales/de_DE.json @@ -42,5 +42,6 @@ "error.fileCopy": "Fehler beim kopieren von Datei/Ordner in %s: %s", "error.deleteFile": "Fehler beim löschen von Datei/Ordner in %s: %s", "command.createBackup": "Backup erstellen", - "error.BackupSetNotSupportedChars": "Der Name des Backup-Sets enthält nicht zulässige Zeichen ( %s )!" + "error.BackupSetNotSupportedChars": "Der Name des Backup-Sets enthält nicht zulässige Zeichen ( %s )!", + "error.passwordDoubleQuotes": "Das Passwort enthält \" (Doppelte Anführungszeichen), diese sind wegen eines Bugs nicht erlaubt. Der Passwortschutz für die Backups wurde deaktivert!" } diff --git a/src/locales/en_US.json b/src/locales/en_US.json index d50e87c..2fdf578 100644 --- a/src/locales/en_US.json +++ b/src/locales/en_US.json @@ -42,5 +42,6 @@ "error.fileCopy": "Error on file/folder copy in %s: %s", "error.deleteFile": "Error on file/folder delete in %s: %s", "command.createBackup": "Create backup", - "error.BackupSetNotSupportedChars": "Backup set name does contain not allowed characters ( %s )!" + "error.BackupSetNotSupportedChars": "Backup set name does contain not allowed characters ( %s )!", + "error.passwordDoubleQuotes": "Password contains \" (double quotes), these are not allowed because of a bug. Password protection for the backup is deactivated!" } From e9e361dbdf98dd8e8d2864de453f25448bb5f863 Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Sat, 8 Jul 2023 13:21:34 +0200 Subject: [PATCH 5/7] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65bebf4..1927674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +- Add: Workaround for bug #132 with `"` (double quotes) in the password where zip files with such a password can no longer be opened + ## not released - Fix: #51 for translation zh_CN From 97823d5d2b439392c5ebede30d4de57f9f7205bb Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Sat, 8 Jul 2023 15:29:57 +0200 Subject: [PATCH 6/7] Correct cahngelog entry --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cda65f..667345e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,9 @@ # Changelog -- Add: Workaround for bug #132 with `"` (double quotes) in the password where zip files with such a password can no longer be opened - ## not released +- Add: Workaround for bug #132 with `"` (double quotes) in the password where zip files with such a password can no longer be opened + ## v1.3.2 (2023-06-02) - Fix: #51 for translation zh_CN From 907633c92021e34de40378238d5ceb075a35b345 Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Sat, 8 Jul 2023 15:30:52 +0200 Subject: [PATCH 7/7] bump version 1.3.3 --- CHANGELOG.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/manifest.json | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 667345e..1f4a783 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## not released +## v1.3.3 (2023-07-08) + - Add: Workaround for bug #132 with `"` (double quotes) in the password where zip files with such a password can no longer be opened ## v1.3.2 (2023-06-02) diff --git a/package-lock.json b/package-lock.json index 2bcda27..a786bb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "joplin-plugin-backup", - "version": "1.3.2", + "version": "1.3.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "joplin-plugin-backup", - "version": "1.3.2", + "version": "1.3.3", "license": "MIT", "dependencies": { "@types/i18n": "^0.13.6", diff --git a/package.json b/package.json index a6c14fc..2ef60f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "joplin-plugin-backup", - "version": "1.3.2", + "version": "1.3.3", "scripts": { "dist": "webpack --joplin-plugin-config buildMain && webpack --joplin-plugin-config buildExtraScripts && webpack --joplin-plugin-config createArchive", "prepare": "npm run dist && husky install", diff --git a/src/manifest.json b/src/manifest.json index b5ee984..7b3a289 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 1, "id": "io.github.jackgruber.backup", "app_min_version": "2.1.3", - "version": "1.3.2", + "version": "1.3.3", "name": "Simple Backup", "description": "Plugin to create manual and automatic backups.", "author": "JackGruber",