From 52b0190356f719ff228b1e5dd6ca4cc92e99714b Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 8 Jun 2018 17:35:32 +0200 Subject: [PATCH] feat: use browser.contentScripts API in Firefox This change enables Firefox users to disable creation of window.ipfs, solving fingerprinting issue raised in: https://github.com/ipfs-shipyard/ipfs-companion/issues/451 The key difference between tabs.executeScript and contentScripts API is that the latter provides guarantee to execute before anything else. Chrome does not provide contentScripts API and we need to statically load content_script via manifest. More info on the underlying issue: https://github.com/ipfs-shipyard/ipfs-companion/pull/368 https://github.com/ipfs-shipyard/ipfs-companion/issues/362#issuecomment-362231167 --- add-on/manifest.firefox.json | 1 + add-on/src/lib/ipfs-companion.js | 38 +++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/add-on/manifest.firefox.json b/add-on/manifest.firefox.json index 0865fb696..30d74bb1d 100644 --- a/add-on/manifest.firefox.json +++ b/add-on/manifest.firefox.json @@ -18,6 +18,7 @@ "default_title": "__MSG_pageAction_titleNonIpfs__", "default_popup": "dist/popup/page-action/index.html" }, + "content_scripts": null, "protocol_handlers": [ { "protocol": "web+dweb", diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 64b04d769..bd30dffd7 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -30,6 +30,7 @@ module.exports = async function init () { var contextMenus var apiStatusUpdateInterval var ipfsProxy + var ipfsProxyContentScript const idleInSecs = 5 * 60 const browserActionPortName = 'browser-action-port' @@ -60,6 +61,7 @@ module.exports = async function init () { }) modifyRequest = createRequestModifier(getState, dnsLink, ipfsPathValidator, runtime) ipfsProxy = createIpfsProxy(() => ipfs, getState) + ipfsProxyContentScript = await registerIpfsProxyContentScript() registerListeners() await setApiStatusUpdateInterval(options.ipfsApiPollMs) await storeMissingOptions( @@ -95,6 +97,33 @@ module.exports = async function init () { } } + // Register Content Script responsible for loading window.ipfs (ipfsProxy) + // + // The key difference between tabs.executeScript and contentScripts API + // is the latter provides guarantee to execute before anything else. + // https://github.com/ipfs-shipyard/ipfs-companion/issues/451#issuecomment-382669093 + async function registerIpfsProxyContentScript (previousHandle) { + previousHandle = previousHandle || ipfsProxyContentScript + if (previousHandle) { + previousHandle.unregister() + } + if (!state.ipfsProxy || !browser.contentScripts) { + // no-op if window.ipfs is disabled in Preferences + // or if runtime has no contentScript API + // (Chrome loads content script via manifest) + return + } + const newHandle = await browser.contentScripts.register({ + matches: [''], + js: [ + {file: '/dist/contentScripts/ipfs-proxy/content.js'} + ], + allFrames: true, + runAt: 'document_start' + }) + return newHandle + } + // HTTP Request Hooks // =================================================================== @@ -544,13 +573,16 @@ module.exports = async function init () { case 'useCustomGateway': state.redirect = change.newValue break + case 'ipfsProxy': + state[key] = change.newValue + ipfsProxyContentScript = await registerIpfsProxyContentScript() + break case 'linkify': case 'catchUnhandledProtocols': case 'displayNotifications': case 'automaticMode': case 'dnslink': case 'preloadAtPublicGateway': - case 'ipfsProxy': state[key] = change.newValue break } @@ -616,6 +648,10 @@ module.exports = async function init () { contextMenus = null ipfsProxy.destroy() ipfsProxy = null + if (ipfsProxyContentScript) { + ipfsProxyContentScript.unregister() + ipfsProxyContentScript = null + } await destroyIpfsClient() } }