mirror of https://github.com/keeweb/keeweb.git
plugin signature validation
This commit is contained in:
parent
c86042b885
commit
31f5f2d88a
|
@ -46,9 +46,7 @@ const SettingsStore = {
|
|||
return new Promise(resolve => {
|
||||
if (Launcher) {
|
||||
const settingsFile = Launcher.getUserDataPath(this.fileName(key));
|
||||
if (typeof data !== 'string') {
|
||||
data = JSON.stringify(data);
|
||||
}
|
||||
data = JSON.stringify(data);
|
||||
Launcher.writeFile(settingsFile, data, err => {
|
||||
if (err) {
|
||||
logger.error(`Error saving ${key}`, err);
|
||||
|
|
|
@ -19,49 +19,58 @@ const PluginGallery = {
|
|||
}
|
||||
this.loading = true;
|
||||
this.loadError = false;
|
||||
const ts = this.logger.ts();
|
||||
return new Promise(resolve => {
|
||||
const ts = this.logger.ts();
|
||||
this.logger.debug('Loading plugins...');
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', Links.Plugins + '/plugins.json?_=' + Date.now());
|
||||
xhr.responseType = 'text';
|
||||
xhr.responseType = 'json';
|
||||
xhr.send();
|
||||
xhr.addEventListener('load', () => {
|
||||
const data = xhr.response;
|
||||
const gallery = JSON.parse(data);
|
||||
const dataToVerify = data.replace(gallery.signature, '');
|
||||
SignatureVerifier.verify(
|
||||
kdbxweb.ByteUtils.stringToBytes(dataToVerify),
|
||||
gallery.signature
|
||||
).then(isValid => {
|
||||
if (!isValid) {
|
||||
this.logger.error('JSON signature invalid');
|
||||
this.galleryLoadError = true;
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
this.logger.debug(`Loaded ${gallery.plugins.length} plugins`, this.logger.ts(ts));
|
||||
if (Launcher) {
|
||||
this.saveGallery(data);
|
||||
}
|
||||
resolve(gallery);
|
||||
}).catch(e => {
|
||||
this.logger.error('Error verifying plugins signature', e);
|
||||
resolve();
|
||||
});
|
||||
resolve(data);
|
||||
});
|
||||
xhr.addEventListener('error', () => {
|
||||
this.logger.error('Network error loading plugins');
|
||||
resolve();
|
||||
});
|
||||
}).then(gallery => {
|
||||
this.loading = false;
|
||||
this.loadError = !gallery;
|
||||
if (gallery) {
|
||||
this.gallery = gallery;
|
||||
}).then(data => {
|
||||
return this.verifySignature(data).then(gallery => {
|
||||
this.loading = false;
|
||||
this.loadError = !gallery;
|
||||
if (gallery) {
|
||||
this.logger.debug(`Loaded ${gallery.plugins.length} plugins`, this.logger.ts(ts));
|
||||
this.gallery = gallery;
|
||||
this.saveGallery(gallery);
|
||||
}
|
||||
Backbone.trigger('plugin-gallery-load-complete');
|
||||
return gallery;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
verifySignature(gallery) {
|
||||
const dataToVerify = JSON.stringify(gallery, null, 2).replace(gallery.signature, '');
|
||||
return SignatureVerifier.verify(
|
||||
kdbxweb.ByteUtils.stringToBytes(dataToVerify),
|
||||
gallery.signature
|
||||
).then(isValid => {
|
||||
if (isValid) {
|
||||
return gallery;
|
||||
}
|
||||
Backbone.trigger('plugin-gallery-load-complete');
|
||||
return gallery;
|
||||
this.logger.error('JSON signature invalid');
|
||||
}).catch(e => {
|
||||
this.logger.error('Error verifying plugins signature', e);
|
||||
});
|
||||
},
|
||||
|
||||
getCachedGallery() {
|
||||
const ts = this.logger.ts();
|
||||
return SettingsStore.load('plugin-gallery').then(data => {
|
||||
return this.verifySignature(data).then(gallery => {
|
||||
this.logger.debug(`Loaded cached plugin gallery`, this.logger.ts(ts));
|
||||
return gallery;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
const Backbone = require('backbone');
|
||||
const Plugin = require('./plugin');
|
||||
const PluginCollection = require('./plugin-collection');
|
||||
const PluginGallery = require('./plugin-gallery');
|
||||
const SettingsStore = require('../comp/settings-store');
|
||||
const RuntimeInfo = require('../comp/runtime-info');
|
||||
const SignatureVerifier = require('../util/signature-verifier');
|
||||
const Logger = require('../util/logger');
|
||||
|
||||
const PluginManager = Backbone.Model.extend({
|
||||
|
@ -29,11 +31,13 @@ const PluginManager = Backbone.Model.extend({
|
|||
if (!state || !state.plugins || !state.plugins.length) {
|
||||
return;
|
||||
}
|
||||
const promises = state.plugins.map(plugin => this.loadPlugin(plugin));
|
||||
return Promise.all(promises).then(loadedPlugins => {
|
||||
const plugins = this.get('plugins');
|
||||
plugins.add(loadedPlugins.filter(plugin => plugin));
|
||||
this.logger.info(`Loaded ${plugins.length} plugins`, this.logger.ts(ts));
|
||||
return PluginGallery.getCachedGallery().then(gallery => {
|
||||
const promises = state.plugins.map(plugin => this.loadPlugin(plugin, gallery));
|
||||
return Promise.all(promises).then(loadedPlugins => {
|
||||
const plugins = this.get('plugins');
|
||||
plugins.add(loadedPlugins.filter(plugin => plugin));
|
||||
this.logger.info(`Loaded ${plugins.length} plugins`, this.logger.ts(ts));
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -159,13 +163,19 @@ const PluginManager = Backbone.Model.extend({
|
|||
return updateNext();
|
||||
},
|
||||
|
||||
loadPlugin(desc) {
|
||||
loadPlugin(desc, gallery) {
|
||||
const plugin = new Plugin({
|
||||
manifest: desc.manifest,
|
||||
url: desc.url,
|
||||
autoUpdate: desc.autoUpdate
|
||||
});
|
||||
return plugin.install(desc.enabled, true)
|
||||
let enabled = desc.enabled;
|
||||
if (enabled) {
|
||||
const galleryPlugin = gallery ? gallery.plugins.find(pl => pl.manifest.name === desc.manifest.name) : null;
|
||||
const expectedPublicKey = galleryPlugin ? galleryPlugin.manifest.publicKey : SignatureVerifier.getPublicKey();
|
||||
enabled = desc.manifest.publicKey === expectedPublicKey;
|
||||
}
|
||||
return plugin.install(enabled, true)
|
||||
.then(() => plugin)
|
||||
.catch(() => plugin);
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue