From 2409e716ef2a9a4ae119301a5fa985dd9d75b084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=27rysiek=27=20Wo=C5=BAniak?= Date: Wed, 31 Jan 2024 21:46:29 +0000 Subject: [PATCH] verifying config.json downloaded via other plugins than fetch (ref. #48) --- service-worker.js | 84 +++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/service-worker.js b/service-worker.js index bb4cf94..c7f4aed 100644 --- a/service-worker.js +++ b/service-worker.js @@ -404,7 +404,7 @@ let executeConfig = (pluginsConfig) => { } -// flag signifying the SW has been initialized already +// flag signifying if the SW has been initialized already var initDone = false // load the plugins @@ -476,10 +476,15 @@ let initServiceWorker = async () => { // TODO: handle in a more elegant way let lrpcBackup = new Map(self.LibResilientPluginConstructors) - // working on a copy of the plugins config so that - // self.LibResilientConfig.plugins remains unmodified - // in case we need it later (for example, when re-loading the config) - executeConfig([...self.LibResilientConfig.plugins]) + try { + // working on a copy of the plugins config so that + // self.LibResilientConfig.plugins remains unmodified + // in case we need it later (for example, when re-loading the config) + executeConfig([...self.LibResilientConfig.plugins]) + } catch (e) { + // cleanup after a failed config execution + self.LibResilientPluginConstructors = new Map(lrpcBackup) + } // inform self.log('service-worker', `service worker initialized.\nstrategy in use: ${self.LibResilientPlugins.map(p=>p.name).join(', ')}`) @@ -507,42 +512,41 @@ let initServiceWorker = async () => { // which signifies that relevant code has been successfully loaded; but there are other failure modes! if (cresponse.headers.get('x-libresilient-method') != 'fetch') { - try { - - // go through the plugins in the new config and check if we already have their constructors - // i.e. if the plugin scripts have already been loaded - let plugins = [...cdata.plugins] - let currentPlugin = plugins.shift() - do { - // plugin constructor not available, meaning: we'd have to importScripts() it - // but we can't since this was not retrieved via fetch(), so we cannot assume - // that the main domain of the website is up and available - // - // if we cache this newly retrieved config.json, next time the service worker gets restarted - // we will end up with an error while trying to run importScripts() for this plugin - // which in turn would lead to the service worker being unregistered - // - // if the main domain is not available, this would mean the website stops working - // even though we *were* able to retrieve the new config.json via plugins! - // so, ignoring this new config.json. - if (!LibResilientPluginConstructors.has(currentPlugin.name)) { - throw new Error(`config.json loaded through transport other than fetch, but specifies not previously loaded plugin: "${currentPlugin.name}"`) - } - // push any dependencies into the array, at the very front - // thus gradually flattening the config - if ("uses" in currentPlugin) { - plugins.unshift(...currentPlugin.uses) - } - // get the next plugin to check - currentPlugin = plugins.shift() - } while ( (currentPlugin !== false) && (currentPlugin !== undefined) ) - - } catch(e) { - self.log( - 'service-worker', - `warning: ${e.message}\nignoring the whole config.json.`) - return false; + // basic structure tests + if ( !verifyConfigData(cdata) ) { + self.log('service-worker', 'config.json loaded through transport other than fetch is invalid, ignoring') + return false } + + // go through the plugins in the new config and check if we already have their constructors + // i.e. if the plugin scripts have already been loaded + let currentPlugin = cdata.plugins.shift() + do { + // plugin constructor not available, meaning: we'd have to importScripts() it + // but we can't since this was not retrieved via fetch(), so we cannot assume + // that the main domain of the website is up and available + // + // if we cache this newly retrieved config.json, next time the service worker gets restarted + // we will end up with an error while trying to run importScripts() for this plugin + // which in turn would lead to the service worker being unregistered + // + // if the main domain is not available, this would mean the website stops working + // even though we *were* able to retrieve the new config.json via plugins! + // so, ignoring this new config.json. + if (!LibResilientPluginConstructors.has(currentPlugin.name)) { + self.log( + 'service-worker', + `warning: config.json loaded through transport other than fetch, but specifies not previously loaded plugin: "${currentPlugin.name}"\nignoring the whole config.json.`) + return false; + } + // push any dependencies into the array, at the very front + // thus gradually flattening the config + if ("uses" in currentPlugin) { + cdata.plugins.unshift(...currentPlugin.uses) + } + // get the next plugin to check + currentPlugin = cdata.plugins.shift() + } while ( (currentPlugin !== false) && (currentPlugin !== undefined) ) } self.log('service-worker', `config.json successfully retrieved through plugins; caching.`)