disabled desktop update archives

This commit is contained in:
antelle 2021-01-08 22:54:45 +01:00
parent bee96ebf15
commit 75fa636366
No known key found for this signature in database
GPG Key ID: 63C9777AAB7C563C
20 changed files with 176 additions and 296 deletions

View File

@ -95,11 +95,6 @@ jobs:
with: with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm
- name: Upload update artifact
uses: actions/upload-artifact@v1
with:
name: UpdateDesktop.zip
path: dist/desktop/UpdateDesktop.zip
darwin: darwin:
runs-on: macos-latest runs-on: macos-latest
@ -124,9 +119,6 @@ jobs:
path: dist path: dist
- name: Install npm modules - name: Install npm modules
run: npm ci run: npm ci
- name: Install desktop npm modules
working-directory: desktop
run: npm ci
- name: Install grunt - name: Install grunt
run: sudo npm i -g grunt-cli run: sudo npm i -g grunt-cli
- name: Write secrets - name: Write secrets
@ -179,9 +171,6 @@ jobs:
path: dist path: dist
- name: Install npm modules - name: Install npm modules
run: npm ci run: npm ci
- name: Install desktop npm modules
working-directory: desktop
run: npm ci
- name: Install grunt - name: Install grunt
run: npm i -g grunt-cli run: npm i -g grunt-cli
- name: Write secrets - name: Write secrets
@ -328,11 +317,6 @@ jobs:
with: with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip
path: assets path: assets
- name: Download update artifact
uses: actions/download-artifact@v1
with:
name: UpdateDesktop.zip
path: assets
- name: Zip html - name: Zip html
working-directory: html working-directory: html
run: zip -vr ../assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.html.zip . run: zip -vr ../assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.html.zip .
@ -505,15 +489,6 @@ jobs:
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
- name: Upload update asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/UpdateDesktop.zip
asset_name: UpdateDesktop.zip
asset_content_type: application/octet-stream
- name: Upload verify.sign asset - name: Upload verify.sign asset
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:

View File

@ -138,18 +138,6 @@ module.exports = function (grunt) {
expand: true, expand: true,
nonull: true nonull: true
}, },
'desktop-update': {
cwd: 'tmp/desktop/keeweb-linux-x64/resources/',
src: 'app.asar',
dest: 'tmp/desktop/update/',
expand: true,
nonull: true
},
'desktop-update-helper': {
src: ['helper/darwin/KeeWebHelper', 'helper/win32/KeeWebHelper.exe'],
dest: 'tmp/desktop/update/',
nonull: true
},
'desktop-darwin-helper-x64': { 'desktop-darwin-helper-x64': {
src: 'helper/darwin/KeeWebHelper', src: 'helper/darwin/KeeWebHelper',
dest: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app/Contents/Resources/', dest: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app/Contents/Resources/',
@ -461,13 +449,6 @@ module.exports = function (grunt) {
options: { options: {
level: 6 level: 6
}, },
'desktop-update': {
options: {
archive: 'dist/desktop/UpdateDesktop.zip',
comment: zipCommentPlaceholder
},
files: [{ cwd: 'tmp/desktop/update', src: '**', expand: true, nonull: true }]
},
'win32-x64': { 'win32-x64': {
options: { archive: `dist/desktop/KeeWeb-${pkg.version}.win.x64.zip` }, options: { archive: `dist/desktop/KeeWeb-${pkg.version}.win.x64.zip` },
files: [{ cwd: 'tmp/desktop/KeeWeb-win32-x64', src: '**', expand: true }] files: [{ cwd: 'tmp/desktop/KeeWeb-win32-x64', src: '**', expand: true }]
@ -599,35 +580,6 @@ module.exports = function (grunt) {
] ]
} }
}, },
'sign-archive': {
'desktop-update': {
options: {
file: 'dist/desktop/UpdateDesktop.zip',
signature: zipCommentPlaceholder
}
}
},
'sign-desktop-files': {
'desktop-update': {
options: {
path: 'tmp/desktop/update'
}
}
},
'validate-desktop-update': {
desktop: {
options: {
file: 'dist/desktop/UpdateDesktop.zip',
expected: [
'app.asar',
'helper/darwin/KeeWebHelper',
'helper/win32/KeeWebHelper.exe'
],
expectedCount: 7,
publicKey: 'app/resources/public-key.pem'
}
}
},
'osx-sign': { 'osx-sign': {
options: { options: {
get identity() { get identity() {
@ -758,10 +710,7 @@ module.exports = function (grunt) {
sign: 'dist/desktop/Verify.sign.sha256' sign: 'dist/desktop/Verify.sign.sha256'
}, },
files: { files: {
'dist/desktop/Verify.sha256': [ 'dist/desktop/Verify.sha256': ['dist/desktop/KeeWeb-*']
'dist/desktop/KeeWeb-*',
'dist/desktop/UpdateDesktop.zip'
]
} }
} }
}, },

View File

@ -1,3 +1,4 @@
import kdbxweb from 'kdbxweb';
import { Events } from 'framework/events'; import { Events } from 'framework/events';
import { RuntimeInfo } from 'const/runtime-info'; import { RuntimeInfo } from 'const/runtime-info';
import { Transport } from 'comp/browser/transport'; import { Transport } from 'comp/browser/transport';
@ -15,7 +16,6 @@ const Updater = {
UpdateInterval: 1000 * 60 * 60 * 24, UpdateInterval: 1000 * 60 * 60 * 24,
MinUpdateTimeout: 500, MinUpdateTimeout: 500,
MinUpdateSize: 10000, MinUpdateSize: 10000,
UpdateCheckFiles: ['app.asar'],
nextCheckTimeout: null, nextCheckTimeout: null,
updateCheckDate: new Date(0), updateCheckDate: new Date(0),
enabled: Launcher && Launcher.updaterEnabled(), enabled: Launcher && Launcher.updaterEnabled(),
@ -34,7 +34,7 @@ const Updater = {
updateInProgress() { updateInProgress() {
return ( return (
UpdateModel.status === 'checking' || UpdateModel.status === 'checking' ||
['downloading', 'extracting'].indexOf(UpdateModel.updateStatus) >= 0 ['downloading', 'extracting', 'updating'].indexOf(UpdateModel.updateStatus) >= 0
); );
}, },
@ -180,21 +180,46 @@ const Updater = {
} }
UpdateModel.set({ updateStatus: 'downloading', updateError: null }); UpdateModel.set({ updateStatus: 'downloading', updateError: null });
logger.info('Downloading update', ver); logger.info('Downloading update', ver);
const updateAssetName = this.getUpdateAssetName(ver);
if (!updateAssetName) {
logger.error('Empty updater asset name for', Launcher.platform(), Launcher.arch());
return;
}
const updateUrlBasePath = Links.UpdateBasePath.replace('{ver}', ver);
const updateAssetUrl = updateUrlBasePath + updateAssetName;
const useCache = !startedByUser;
Transport.httpGet({ Transport.httpGet({
url: Links.UpdateDesktop.replace('{ver}', ver), url: updateAssetUrl,
file: 'KeeWeb-' + ver + '.zip', file: updateAssetName,
cache: !startedByUser, cleanupOldFiles: true,
success: (filePath) => { cache: useCache,
UpdateModel.set({ updateStatus: 'extracting' }); success: (assetFilePath) => {
logger.info('Extracting update file', this.UpdateCheckFiles, filePath); logger.info('Downloading update signatures');
this.extractAppUpdate(filePath, (err) => { Transport.httpGet({
url: updateUrlBasePath + 'Verify.sign.sha256',
text: true,
file: updateAssetName + '.sign',
cleanupOldFiles: true,
cache: useCache,
success: (assetFileSignaturePath) => {
this.verifySignature(assetFilePath, updateAssetName, (err, valid) => {
if (err) { if (err) {
logger.error('Error extracting update', err);
UpdateModel.set({ UpdateModel.set({
updateStatus: 'error', updateStatus: 'error',
updateError: 'Error extracting update' updateError: 'Error verifying update signature'
}); });
} else { return;
}
if (!valid) {
UpdateModel.set({
updateStatus: 'error',
updateError: 'Invalid update signature'
});
Launcher.deleteFile(assetFilePath);
Launcher.deleteFile(assetFileSignaturePath);
return;
}
logger.info('Update is ready', assetFilePath);
UpdateModel.set({ updateStatus: 'ready', updateError: null }); UpdateModel.set({ updateStatus: 'ready', updateError: null });
if (!startedByUser) { if (!startedByUser) {
Events.emit('update-app'); Events.emit('update-app');
@ -202,6 +227,14 @@ const Updater = {
if (typeof successCallback === 'function') { if (typeof successCallback === 'function') {
successCallback(); successCallback();
} }
});
},
error(e) {
logger.error('Error downloading update signatures', e);
UpdateModel.set({
updateStatus: 'error',
updateError: 'Error downloading update signatures'
});
} }
}); });
}, },
@ -215,61 +248,64 @@ const Updater = {
}); });
}, },
extractAppUpdate(updateFile, cb) { verifySignature(assetFilePath, assetName, callback) {
const expectedFiles = this.UpdateCheckFiles; logger.info('Verifying update signature', assetName);
const appPath = Launcher.getUserDataPath(); const fs = Launcher.req('fs');
const StreamZip = Launcher.req('node-stream-zip'); const signaturesTxt = fs.readFileSync(assetFilePath + '.sign', 'utf8');
StreamZip.setFs(Launcher.req('original-fs')); const assetSignatureLine = signaturesTxt
const zip = new StreamZip({ file: updateFile, storeEntries: true }); .split('\n')
zip.on('error', cb); .find((line) => line.endsWith(assetName));
zip.on('ready', () => { if (!assetSignatureLine) {
const containsAll = expectedFiles.every((expFile) => { logger.error('Signature not found for asset', assetName);
const entry = zip.entry(expFile); callback('Asset signature not found');
return entry && entry.isFile; return;
});
if (!containsAll) {
return cb('Bad archive');
} }
this.validateArchiveSignature(updateFile, zip) const signature = kdbxweb.ByteUtils.hexToBytes(assetSignatureLine.split(' ')[0]);
.then(() => { const fileBytes = fs.readFileSync(assetFilePath);
zip.extract(null, appPath, (err) => { SignatureVerifier.verify(fileBytes, signature)
zip.close();
if (err) {
return cb(err);
}
Launcher.deleteFile(updateFile);
cb();
});
})
.catch((e) => { .catch((e) => {
return cb('Invalid archive: ' + e); logger.error('Error verifying signature', e);
}); callback('Error verifying signature');
})
.then((valid) => {
logger.info(`Update asset signature is ${valid ? 'valid' : 'invalid'}`);
callback(undefined, valid);
}); });
}, },
validateArchiveSignature(archivePath, zip) { getUpdateAssetName(ver) {
if (!zip.comment) { const platform = Launcher.platform();
return Promise.reject('No comment in ZIP'); const arch = Launcher.arch();
switch (platform) {
case 'win32':
switch (arch) {
case 'x64':
return `KeeWeb-${ver}.win.x64.exe`;
case 'ia32':
return `KeeWeb-${ver}.win.ia32.exe`;
case 'arm64':
return `KeeWeb-${ver}.win.arm64.exe`;
} }
if (zip.comment.length !== 512) { break;
return Promise.reject('Bad comment length in ZIP: ' + zip.comment.length); case 'darwin':
switch (arch) {
case 'x64':
return `KeeWeb-${ver}.mac.x64.dmg`;
case 'arm64':
return `KeeWeb-${ver}.mac.arm64.dmg`;
} }
try { break;
const zipFileData = Launcher.req('fs').readFileSync(archivePath);
const dataToVerify = zipFileData.slice(0, zip.centralDirectory.headerOffset + 22);
const signature = window.Buffer.from(zip.comment, 'hex');
return SignatureVerifier.verify(dataToVerify, signature)
.catch(() => {
throw new Error('Error verifying signature');
})
.then((isValid) => {
if (!isValid) {
throw new Error('Invalid signature');
} }
}); return undefined;
} catch (err) { },
return Promise.reject(err.toString());
installAndRestart() {
if (!Launcher) {
return;
} }
const updateAssetName = this.getUpdateAssetName(UpdateModel.lastVersion);
const updateFilePath = Transport.cacheFilePath(updateAssetName);
Launcher.requestRestartAndUpdate(updateFilePath);
} }
}; };

View File

@ -1,15 +1,33 @@
import { Launcher } from 'comp/launcher'; import { Launcher } from 'comp/launcher';
import { Logger } from 'util/logger'; import { Logger } from 'util/logger';
import { noop } from 'util/fn'; import { noop } from 'util/fn';
import { StringFormat } from 'util/formatting/string-format';
const logger = new Logger('transport'); const logger = new Logger('transport');
const Transport = { const Transport = {
cacheFilePath(fileName) {
return Launcher.getTempPath(fileName);
},
httpGet(config) { httpGet(config) {
let tmpFile; let tmpFile;
const fs = Launcher.req('fs'); const fs = Launcher.req('fs');
if (config.file) { if (config.file) {
tmpFile = Launcher.getTempPath(config.file); const baseTempPath = Launcher.getTempPath();
if (config.cleanupOldFiles) {
const allFiles = fs.readdirSync(baseTempPath);
for (const file of allFiles) {
if (
file !== config.file &&
StringFormat.replaceVersion(file, '0') ===
StringFormat.replaceVersion(config.file, '0')
) {
fs.unlinkSync(Launcher.joinPath(baseTempPath, file));
}
}
}
tmpFile = Launcher.joinPath(baseTempPath, config.file);
if (fs.existsSync(tmpFile)) { if (fs.existsSync(tmpFile)) {
try { try {
if (config.cache && fs.statSync(tmpFile).size > 0) { if (config.cache && fs.statSync(tmpFile).size > 0) {
@ -62,8 +80,10 @@ const Transport = {
}); });
res.on('end', () => { res.on('end', () => {
data = window.Buffer.concat(data); data = window.Buffer.concat(data);
if (config.json) { if (config.text || config.json) {
data = data.toString('utf8'); data = data.toString('utf8');
}
if (config.json) {
try { try {
data = JSON.parse(data); data = JSON.parse(data);
} catch (e) { } catch (e) {

View File

@ -17,6 +17,9 @@ const Launcher = {
platform() { platform() {
return process.platform; return process.platform;
}, },
arch() {
return process.arch;
},
electron() { electron() {
return this.req('electron'); return this.req('electron');
}, },
@ -55,7 +58,15 @@ const Launcher = {
return this.joinPath(this.userDataPath, fileName || ''); return this.joinPath(this.userDataPath, fileName || '');
}, },
getTempPath(fileName) { getTempPath(fileName) {
return this.joinPath(this.remoteApp().getPath('temp'), fileName || ''); let tempPath = this.joinPath(this.remoteApp().getPath('temp'), 'KeeWeb');
const fs = this.req('fs');
if (!fs.existsSync(tempPath)) {
fs.mkdirSync(tempPath);
}
if (fileName) {
tempPath = this.joinPath(tempPath, fileName);
}
return tempPath;
}, },
getDocumentsPath(fileName) { getDocumentsPath(fileName) {
return this.joinPath(this.remoteApp().getPath('documents'), fileName || ''); return this.joinPath(this.remoteApp().getPath('documents'), fileName || '');
@ -164,18 +175,18 @@ const Launcher = {
requestExit() { requestExit() {
const app = this.remoteApp(); const app = this.remoteApp();
app.setHookBeforeQuitEvent(false); app.setHookBeforeQuitEvent(false);
if (this.restartPending) { if (this.pendingUpdateFile) {
app.restartApp(); app.restartAndUpdate(this.pendingUpdateFile);
} else { } else {
app.quit(); app.quit();
} }
}, },
requestRestart() { requestRestartAndUpdate(updateFilePath) {
this.restartPending = true; this.pendingUpdateFile = updateFilePath;
this.requestExit(); this.requestExit();
}, },
cancelRestart() { cancelRestart() {
this.restartPending = false; this.pendingUpdateFile = undefined;
}, },
setClipboardText(text) { setClipboardText(text) {
return this.electron().clipboard.writeText(text); return this.electron().clipboard.writeText(text);

View File

@ -7,7 +7,7 @@ const Links = {
License: 'https://github.com/keeweb/keeweb/blob/master/LICENSE', License: 'https://github.com/keeweb/keeweb/blob/master/LICENSE',
LicenseApache: 'https://opensource.org/licenses/Apache-2.0', LicenseApache: 'https://opensource.org/licenses/Apache-2.0',
LicenseLinkCCBY40: 'https://creativecommons.org/licenses/by/4.0/', LicenseLinkCCBY40: 'https://creativecommons.org/licenses/by/4.0/',
UpdateDesktop: 'https://github.com/keeweb/keeweb/releases/download/v{ver}/UpdateDesktop.zip', UpdateBasePath: 'https://github.com/keeweb/keeweb/releases/download/v{ver}/',
ReleaseNotes: 'https://github.com/keeweb/keeweb/blob/master/release-notes.md#release-notes', ReleaseNotes: 'https://github.com/keeweb/keeweb/blob/master/release-notes.md#release-notes',
SelfHostedDropbox: 'https://github.com/keeweb/keeweb#self-hosting', SelfHostedDropbox: 'https://github.com/keeweb/keeweb#self-hosting',
UpdateJson: 'https://app.keeweb.info/update.json', UpdateJson: 'https://app.keeweb.info/update.json',

View File

@ -373,7 +373,7 @@
"setGenExtractingUpdate": "Extracting update...", "setGenExtractingUpdate": "Extracting update...",
"setGenCheckErr": "There was an error downloading new version", "setGenCheckErr": "There was an error downloading new version",
"setGenNeverChecked": "Never checked for updates", "setGenNeverChecked": "Never checked for updates",
"setGenRestartToUpdate": "Restart the app to update", "setGenRestartToUpdate": "Restart KeeWeb to update",
"setGenDownloadAndRestart": "Download update and restart", "setGenDownloadAndRestart": "Download update and restart",
"setGenAppearance": "Appearance", "setGenAppearance": "Appearance",
"setGenTheme": "Theme", "setGenTheme": "Theme",

View File

@ -29,6 +29,10 @@ const StringFormat = {
pascalCase(str) { pascalCase(str) {
return this.capFirst(str.replace(this.camelCaseRegex, (match) => match[1].toUpperCase())); return this.capFirst(str.replace(this.camelCaseRegex, (match) => match[1].toUpperCase()));
},
replaceVersion(str, replacement) {
return str.replace(/\d+\.\d+\.\d+/g, replacement);
} }
}; };

View File

@ -55,7 +55,7 @@ class SettingsGeneralView extends View {
'changeFieldLabelDblClickAutoType', 'changeFieldLabelDblClickAutoType',
'change .settings__general-titlebar-style': 'changeTitlebarStyle', 'change .settings__general-titlebar-style': 'changeTitlebarStyle',
'click .settings__general-update-btn': 'checkUpdate', 'click .settings__general-update-btn': 'checkUpdate',
'click .settings__general-restart-btn': 'restartApp', 'click .settings__general-restart-btn': 'installUpdateAndRestart',
'click .settings__general-download-update-btn': 'downloadUpdate', 'click .settings__general-download-update-btn': 'downloadUpdate',
'click .settings__general-update-found-btn': 'installFoundUpdate', 'click .settings__general-update-found-btn': 'installFoundUpdate',
'change .settings__general-prv-check': 'changeStorageEnabled', 'change .settings__general-prv-check': 'changeStorageEnabled',
@ -421,9 +421,9 @@ class SettingsGeneralView extends View {
Events.emit('refresh'); Events.emit('refresh');
} }
restartApp() { installUpdateAndRestart() {
if (Launcher) { if (Launcher) {
Launcher.requestRestart(); Updater.installAndRestart();
} else { } else {
window.location.reload(); window.location.reload();
} }
@ -435,7 +435,7 @@ class SettingsGeneralView extends View {
installFoundUpdate() { installFoundUpdate() {
Updater.update(true, () => { Updater.update(true, () => {
Launcher.requestRestart(); Updater.installAndRestart();
}); });
} }

View File

@ -37,7 +37,6 @@
{{#if isDesktop}} {{#if isDesktop}}
<h3>Desktop modules</h3> <h3>Desktop modules</h3>
<ul> <ul>
<li><a href="https://github.com/antelle/node-stream-zip" target="_blank">node-stream-zip</a><span class="muted-color">, node.js library for fast reading of large ZIPs, &copy; 2015 Antelle</span></li>
<li><a href="https://github.com/ranisalt/node-argon2" target="_blank">node-argon2</a><span class="muted-color">, node.js bindings for Argon2 hashing algorithm, &copy; 2015 Ranieri Althoff</span></li> <li><a href="https://github.com/ranisalt/node-argon2" target="_blank">node-argon2</a><span class="muted-color">, node.js bindings for Argon2 hashing algorithm, &copy; 2015 Ranieri Althoff</span></li>
<li><a href="https://github.com/tessel/node-usb" target="_blank">node-usb</a><span class="muted-color">, improved USB library for Node.js, &copy; 2012 Nonolith Labs, LLC</span></li> <li><a href="https://github.com/tessel/node-usb" target="_blank">node-usb</a><span class="muted-color">, improved USB library for Node.js, &copy; 2012 Nonolith Labs, LLC</span></li>
<li><a href="https://github.com/atom/node-keytar" target="_blank">node-keytar</a><span class="muted-color">, native password node module, &copy; 2013 GitHub Inc.</span></li> <li><a href="https://github.com/atom/node-keytar" target="_blank">node-keytar</a><span class="muted-color">, native password node module, &copy; 2013 GitHub Inc.</span></li>

View File

@ -1,28 +0,0 @@
module.exports = function (grunt) {
grunt.registerMultiTask('sign-archive', 'Signs archive with a private key', function () {
const done = this.async();
const fs = require('fs');
const sign = require('../util/sign');
const file = fs.readFileSync(this.options().file);
const ix = file.toString('binary').lastIndexOf(this.options().signature);
if (ix < 0) {
grunt.warn('Signature placeholder not found');
return;
}
const data = file.slice(0, ix);
sign(grunt, data).then((signature) => {
signature = Buffer.from(signature.toString('hex'), 'binary');
if (
signature.byteLength !== Buffer.from(this.options().signature, 'binary').byteLength
) {
grunt.warn('Bad signature length');
return;
}
for (let i = 0; i < signature.byteLength; i++) {
file[ix + i] = signature[i];
}
fs.writeFileSync(this.options().file, file);
done();
});
});
};

View File

@ -1,63 +0,0 @@
module.exports = function (grunt) {
grunt.registerMultiTask(
'validate-desktop-update',
'Validates desktop update package',
function () {
const path = require('path');
const crypto = require('crypto');
const fs = require('fs');
const done = this.async();
const StreamZip = require(path.resolve(
__dirname,
'../../desktop/node_modules/node-stream-zip'
));
const zip = new StreamZip({ file: this.options().file, storeEntries: true });
const expFiles = this.options().expected;
const expFilesCount = this.options().expectedCount;
const publicKey = fs.readFileSync(this.options().publicKey, 'binary');
const zipFileData = fs.readFileSync(this.options().file);
zip.on('error', (err) => {
grunt.warn(err);
});
zip.on('ready', () => {
let valid = true;
if (!zip.comment) {
grunt.warn('No comment in ZIP');
return;
}
if (zip.comment.length !== 512) {
grunt.warn('Bad comment length in ZIP');
return;
}
const verify = crypto.createVerify('RSA-SHA256');
verify.write(zipFileData.slice(0, zip.centralDirectory.headerOffset + 22));
verify.end();
const signature = Buffer.from(zip.comment, 'hex');
if (!verify.verify(publicKey, signature)) {
grunt.warn('Invalid ZIP signature');
return;
}
if (zip.entriesCount !== expFilesCount) {
grunt.warn(
`ZIP contains ${zip.entriesCount} entries, expected ${expFilesCount}`
);
valid = false;
}
expFiles.forEach((entry) => {
try {
if (!zip.entryDataSync(entry)) {
grunt.warn('Corrupted entry in desktop update archive: ' + entry);
valid = false;
}
} catch (e) {
grunt.warn('Entry not found in desktop update archive: ' + entry);
valid = false;
}
});
if (valid) {
done();
}
});
}
);
};

View File

@ -13,7 +13,7 @@ let mainWindow = null;
let appIcon = null; let appIcon = null;
let ready = false; let ready = false;
let appReady = false; let appReady = false;
let restartPending = false; let pendingUpdateFilePath;
let mainWindowPosition = {}; let mainWindowPosition = {};
let updateMainWindowPositionTimeout = null; let updateMainWindowPositionTimeout = null;
let mainWindowMaximized = false; let mainWindowMaximized = false;
@ -95,9 +95,8 @@ const settingsPromise = loadSettingsEncryptionKey().then((key) => {
}); });
main.on('window-all-closed', () => { main.on('window-all-closed', () => {
if (restartPending) { if (pendingUpdateFilePath) {
main.relaunch(); exitAndStartUpdate();
main.exit(0);
} else { } else {
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
main.quit(); main.quit();
@ -160,11 +159,11 @@ main.on('web-contents-created', (event, contents) => {
} }
}); });
}); });
main.restartApp = function () { main.restartAndUpdate = function (updateFilePath) {
restartPending = true; pendingUpdateFilePath = updateFilePath;
mainWindow.close(); mainWindow.close();
setTimeout(() => { setTimeout(() => {
restartPending = false; pendingUpdateFilePath = undefined;
}, 1000); }, 1000);
}; };
main.minimizeApp = function (menuItemLabels) { main.minimizeApp = function (menuItemLabels) {
@ -947,3 +946,10 @@ function httpRequest(config, log, onLoad) {
} }
req.end(); req.end();
} }
function exitAndStartUpdate() {
if (pendingUpdateFilePath) {
// TODO: install the update
main.exit(0);
}
}

View File

@ -1,13 +0,0 @@
{
"name": "KeeWeb",
"version": "1.16.7",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"node-stream-zip": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.4.2.tgz",
"integrity": "sha512-9WbviH8qi0HgHmHbQleydUZToH3vNlN83CyOrhrwGaPzmBPH1SUYUkFyHjWl6GWMMODlM81qgycZEfWm267gKA=="
}
}
}

View File

@ -14,9 +14,5 @@
"url": "http://antelle.net" "url": "http://antelle.net"
}, },
"license": "MIT", "license": "MIT",
"readme": "../README.md", "readme": "../README.md"
"dependencies": {
"node-stream-zip": "^1.4.2"
},
"devDependencies": {}
} }

View File

@ -24,7 +24,6 @@ module.exports = function(grunt) {
'clean:desktop', 'clean:desktop',
'build-desktop-app-content', 'build-desktop-app-content',
'build-desktop-executables-linux', 'build-desktop-executables-linux',
'build-desktop-update',
'build-desktop-archives-linux', 'build-desktop-archives-linux',
'build-desktop-dist-linux' 'build-desktop-dist-linux'
]); ]);

View File

@ -24,15 +24,6 @@ module.exports = function (grunt) {
'string-replace:desktop-public-key' 'string-replace:desktop-public-key'
]); ]);
grunt.registerTask('build-desktop-update', [
'copy:desktop-update',
'copy:desktop-update-helper',
'sign-desktop-files:desktop-update',
'compress:desktop-update',
'sign-archive:desktop-update',
'validate-desktop-update'
]);
grunt.registerTask('build-desktop-executables-linux', [ grunt.registerTask('build-desktop-executables-linux', [
'electron:linux', 'electron:linux',
'chmod:linux-desktop-x64', 'chmod:linux-desktop-x64',
@ -126,7 +117,6 @@ module.exports = function (grunt) {
'clean:desktop', 'clean:desktop',
'build-desktop-app-content', 'build-desktop-app-content',
'build-desktop-executables', 'build-desktop-executables',
'build-desktop-update',
'build-desktop-archives', 'build-desktop-archives',
'build-desktop-dist', 'build-desktop-dist',
'sign-dist' 'sign-dist'

5
package-lock.json generated
View File

@ -11412,11 +11412,6 @@
} }
} }
}, },
"node-stream-zip": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.12.0.tgz",
"integrity": "sha512-HZ3XehqShTFj9gHauRJ3Bri9eiCTOII7/crtXzURtT14NdnOFs9Ia5E82W7z3izVBNx760tqwddxrBJVG52Y1Q=="
},
"noop-logger": { "noop-logger": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",

View File

@ -76,7 +76,6 @@
"morphdom": "^2.6.1", "morphdom": "^2.6.1",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"node-sass": "^5.0.0", "node-sass": "^5.0.0",
"node-stream-zip": "1.12.0",
"normalize.css": "8.0.1", "normalize.css": "8.0.1",
"optimize-css-assets-webpack-plugin": "^5.0.4", "optimize-css-assets-webpack-plugin": "^5.0.4",
"pikaday": "1.8.2", "pikaday": "1.8.2",
@ -109,7 +108,6 @@
"scripts": { "scripts": {
"start": "grunt", "start": "grunt",
"test": "grunt test", "test": "grunt test",
"postinstall": "cd desktop && npm install",
"build-beta": "grunt --beta && cp dist/index.html ../keeweb-beta/index.html && cd ../keeweb-beta && git add index.html && git commit -a -m 'beta' && git push origin master", "build-beta": "grunt --beta && cp dist/index.html ../keeweb-beta/index.html && cd ../keeweb-beta && git add index.html && git commit -a -m 'beta' && git push origin master",
"electron": "cross-env KEEWEB_IS_PORTABLE=0 ELECTRON_DISABLE_SECURITY_WARNINGS=1 KEEWEB_HTML_PATH=http://localhost:8085 electron desktop --no-sandbox", "electron": "cross-env KEEWEB_IS_PORTABLE=0 ELECTRON_DISABLE_SECURITY_WARNINGS=1 KEEWEB_HTML_PATH=http://localhost:8085 electron desktop --no-sandbox",
"dev": "grunt dev", "dev": "grunt dev",

View File

@ -29,4 +29,10 @@ describe('StringFormat', () => {
it('should convert kebab case to pascal case', () => { it('should convert kebab case to pascal case', () => {
expect(StringFormat.pascalCase('aa-bbb-c')).to.eql('AaBbbC'); expect(StringFormat.pascalCase('aa-bbb-c')).to.eql('AaBbbC');
}); });
it('should replace version', () => {
expect(StringFormat.replaceVersion('KeeWeb-1.11.123.x64.dmg', 'ver')).to.eql(
'KeeWeb-ver.x64.dmg'
);
});
}); });