From b7e9aea694e07a1f744259308779187e4fc9bd42 Mon Sep 17 00:00:00 2001 From: Alexei Date: Tue, 24 Sep 2019 11:17:48 -0400 Subject: [PATCH] Enumerate permitted content script messages. --- .eslintrc.yml | 1 - src/js/contentscripts/clobbercookie.js | 5 +- src/js/contentscripts/clobberlocalstorage.js | 5 +- src/js/contentscripts/dnt.js | 2 +- src/js/contentscripts/fingerprinting.js | 43 ++--- src/js/contentscripts/socialwidgets.js | 15 +- src/js/contentscripts/supercookie.js | 6 +- src/js/firstparties/facebook.js | 28 +-- src/js/firstparties/google-search.js | 27 ++- src/js/firstparties/google-static.js | 18 +- src/js/firstparties/twitter.js | 20 +- src/js/storage.js | 28 +-- src/js/webrequest.js | 181 ++++++++++++++----- src/tests/tests/firstparties.js | 2 +- 14 files changed, 241 insertions(+), 140 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index b0096091f6..eb0b562d1d 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -27,7 +27,6 @@ rules: - error - 2 - outerIIFEBody: 0 - SwitchCase: 1 keyword-spacing: error linebreak-style: - error diff --git a/src/js/contentscripts/clobbercookie.js b/src/js/contentscripts/clobbercookie.js index 0ebf6b09c4..66a8570730 100644 --- a/src/js/contentscripts/clobbercookie.js +++ b/src/js/contentscripts/clobbercookie.js @@ -32,7 +32,10 @@ if (window.top == window) { } // TODO race condition; fix waiting on https://crbug.com/478183 -chrome.runtime.sendMessage({ checkLocation: window.FRAME_URL }, function (blocked) { +chrome.runtime.sendMessage({ + type: "checkLocation", + frameUrl: window.FRAME_URL +}, function (blocked) { if (blocked) { var code = '('+ function() { document.__defineSetter__("cookie", function(/*value*/) { }); diff --git a/src/js/contentscripts/clobberlocalstorage.js b/src/js/contentscripts/clobberlocalstorage.js index 9364979266..7ff3528a11 100644 --- a/src/js/contentscripts/clobberlocalstorage.js +++ b/src/js/contentscripts/clobberlocalstorage.js @@ -32,7 +32,10 @@ if (window.top == window) { } // TODO race condition; fix waiting on https://crbug.com/478183 -chrome.runtime.sendMessage({ checkLocation: window.FRAME_URL }, function (blocked) { +chrome.runtime.sendMessage({ + type: "checkLocation", + frameUrl: window.FRAME_URL +}, function (blocked) { if (blocked) { // https://stackoverflow.com/questions/49092423/how-to-break-on-localstorage-changes var code = diff --git a/src/js/contentscripts/dnt.js b/src/js/contentscripts/dnt.js index 5719e7fe9b..d831bd5c63 100644 --- a/src/js/contentscripts/dnt.js +++ b/src/js/contentscripts/dnt.js @@ -50,7 +50,7 @@ if (document instanceof HTMLDocument === false && ( // TODO race condition; fix waiting on https://crbug.com/478183 chrome.runtime.sendMessage({ - checkDNT: true + type: "checkDNT" }, function (enabled) { if (enabled) { window.injectScript(getPageScript()); diff --git a/src/js/contentscripts/fingerprinting.js b/src/js/contentscripts/fingerprinting.js index 53c61b663c..88c81b7d54 100644 --- a/src/js/contentscripts/fingerprinting.js +++ b/src/js/contentscripts/fingerprinting.js @@ -313,28 +313,29 @@ if (document instanceof HTMLDocument === false && ( } // TODO race condition; fix waiting on https://crbug.com/478183 -chrome.runtime.sendMessage({checkEnabled: true}, - function (enabled) { - if (!enabled) { - return; - } - /** - * Communicating to webrequest.js - */ - var event_id = Math.random(); - - // listen for messages from the script we are about to insert - document.addEventListener(event_id, function (e) { - // pass these on to the background page - chrome.runtime.sendMessage({ - fpReport: e.detail - }); +chrome.runtime.sendMessage({ + type: "checkEnabled" +}, function (enabled) { + if (!enabled) { + return; + } + /** + * Communicating to webrequest.js + */ + var event_id = Math.random(); + + // listen for messages from the script we are about to insert + document.addEventListener(event_id, function (e) { + // pass these on to the background page + chrome.runtime.sendMessage({ + type: "fpReport", + data: e.detail }); + }); - window.injectScript(getFpPageScript(), { - event_id: event_id - }); - } -); + window.injectScript(getFpPageScript(), { + event_id: event_id + }); +}); }()); diff --git a/src/js/contentscripts/socialwidgets.js b/src/js/contentscripts/socialwidgets.js index 165b30e660..c8bf7d3d41 100644 --- a/src/js/contentscripts/socialwidgets.js +++ b/src/js/contentscripts/socialwidgets.js @@ -102,7 +102,8 @@ function createReplacementButtonImage(tracker, trackerElem, callback) { // don't have image data cached yet, get it from the background page buttonData.loading = true; chrome.runtime.sendMessage({ - getReplacementButton: buttonData.imagePath + type: "getReplacementButton", + imagePath: buttonData.imagePath }, function (response) { buttonData.buttonUrl = response; // cache image data _createReplacementButtonImageCallback(tracker, trackerElem, callback); @@ -416,7 +417,9 @@ function replaceIndividualButton(tracker) { * replaced */ function getTrackerData(callback) { - chrome.runtime.sendMessage({checkReplaceButton: true}, function(response) { + chrome.runtime.sendMessage({ + type: "checkReplaceButton" + }, function (response) { if (response) { for (const key in response.translations) { TRANSLATIONS[key] = response.translations[key]; @@ -435,7 +438,7 @@ function getTrackerData(callback) { */ function unblockTracker(buttonUrls, callback) { let request = { - unblockWidget: true, + type: "unblockWidget", buttonUrls: buttonUrls }; chrome.runtime.sendMessage(request, callback); @@ -455,9 +458,9 @@ if (document instanceof HTMLDocument === false && ( } chrome.runtime.sendMessage({ - checkWidgetReplacementEnabled: true -}, function (checkWidgetReplacementEnabled) { - if (!checkWidgetReplacementEnabled) { + type: "checkWidgetReplacementEnabled" +}, function (enabled) { + if (!enabled) { return; } initialize(); diff --git a/src/js/contentscripts/supercookie.js b/src/js/contentscripts/supercookie.js index e25aec7c75..6f7c00c671 100644 --- a/src/js/contentscripts/supercookie.js +++ b/src/js/contentscripts/supercookie.js @@ -120,7 +120,8 @@ if (window.top == window) { // could then remove test workarounds like // https://github.com/EFForg/privacybadger/commit/39d5d0899e22d1c451d429e44553c5f9cad7fc46 chrome.runtime.sendMessage({ - checkEnabledAndThirdParty: window.FRAME_URL + type: "checkEnabledAndThirdParty", + frameUrl: window.FRAME_URL }, function (enabledAndThirdParty) { if (!enabledAndThirdParty) { return; @@ -132,7 +133,8 @@ chrome.runtime.sendMessage({ document.addEventListener(event_id_super_cookie, function (e) { // pass these on to the background page (handled by webrequest.js) chrome.runtime.sendMessage({ - superCookieReport: e.detail, + type: "supercookieReport", + data: e.detail, frameUrl: window.FRAME_URL }); }); diff --git a/src/js/firstparties/facebook.js b/src/js/firstparties/facebook.js index c0324c11b2..7b72dd0d05 100644 --- a/src/js/firstparties/facebook.js +++ b/src/js/firstparties/facebook.js @@ -38,19 +38,19 @@ function cleanLink(a) { a.addEventListener("mouseover", function (e) { e.stopImmediatePropagation(); }, true); } -//TODO race condition; fix waiting on https://crbug.com/478183 -chrome.runtime.sendMessage({checkEnabled: true}, - function (enabled) { - if (!enabled) { - return; - } +// TODO race condition; fix waiting on https://crbug.com/478183 +chrome.runtime.sendMessage({ + type: "checkEnabled" +}, function (enabled) { + if (!enabled) { + return; + } - // unwrap wrapped links in the original page - findInAllFrames(fb_wrapped_link).forEach((link) => { - cleanLink(link); - }); + // unwrap wrapped links in the original page + findInAllFrames(fb_wrapped_link).forEach((link) => { + cleanLink(link); + }); - // Execute redirect unwrapping each time new content is added to the page - observeMutations(fb_wrapped_link, cleanLink); - } -); + // Execute redirect unwrapping each time new content is added to the page + observeMutations(fb_wrapped_link, cleanLink); +}); diff --git a/src/js/firstparties/google-search.js b/src/js/firstparties/google-search.js index 92ebbbff3f..bfc0c632a2 100644 --- a/src/js/firstparties/google-search.js +++ b/src/js/firstparties/google-search.js @@ -22,18 +22,17 @@ function cleanLink(a) { a.addEventListener("mousedown", function (e) { e.stopImmediatePropagation(); }, true); } -//TODO race condition; fix waiting on https://crbug.com/478183 -chrome.runtime.sendMessage({checkEnabled: true}, - function (enabled) { - if (!enabled) { - return; - } - - // since the page is rendered all at once, no need to set up a - // mutationObserver or setInterval - findInAllFrames(trap_link).forEach((link) => { - cleanLink(link); - }); - +// TODO race condition; fix waiting on https://crbug.com/478183 +chrome.runtime.sendMessage({ + type: "checkEnabled" +}, function (enabled) { + if (!enabled) { + return; } -); + + // since the page is rendered all at once, no need to set up a + // mutationObserver or setInterval + findInAllFrames(trap_link).forEach((link) => { + cleanLink(link); + }); +}); diff --git a/src/js/firstparties/google-static.js b/src/js/firstparties/google-static.js index 552cb050f2..e9f9c201c4 100644 --- a/src/js/firstparties/google-static.js +++ b/src/js/firstparties/google-static.js @@ -29,13 +29,13 @@ function unwrapAll() { }); } -//TODO race condition; fix waiting on https://crbug.com/478183 -chrome.runtime.sendMessage({checkEnabled: true}, - function (enabled) { - if (!enabled) { - return; - } - unwrapAll(); - setInterval(unwrapAll, 2000); +// TODO race condition; fix waiting on https://crbug.com/478183 +chrome.runtime.sendMessage({ + type: "checkEnabled" +}, function (enabled) { + if (!enabled) { + return; } -); + unwrapAll(); + setInterval(unwrapAll, 2000); +}); diff --git a/src/js/firstparties/twitter.js b/src/js/firstparties/twitter.js index 9dea644297..de3036d70d 100644 --- a/src/js/firstparties/twitter.js +++ b/src/js/firstparties/twitter.js @@ -80,14 +80,14 @@ function unwrapSpecialTwitterURLs() { }); } -//TODO race condition; fix waiting on https://crbug.com/478183 -chrome.runtime.sendMessage({checkEnabled: true}, - function (enabled) { - if (!enabled) { - return; - } - unwrapSpecialTwitterURLs(); - unwrapTwitterURLsInTimeline(); - setInterval(unwrapTwitterURLsInTimeline, 2000); +// TODO race condition; fix waiting on https://crbug.com/478183 +chrome.runtime.sendMessage({ + type: "checkEnabled", +}, function (enabled) { + if (!enabled) { + return; } -); + unwrapSpecialTwitterURLs(); + unwrapTwitterURLsInTimeline(); + setInterval(unwrapTwitterURLsInTimeline, 2000); +}); diff --git a/src/js/storage.js b/src/js/storage.js index 24e4106f0a..eaa509008f 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -233,20 +233,20 @@ BadgerPen.prototype = { function getScore(action) { switch (action) { - case constants.NO_TRACKING: - return 0; - case constants.ALLOW: - return 1; - case constants.BLOCK: - return 2; - case constants.COOKIEBLOCK: - return 3; - case constants.DNT: - return 4; - case constants.USER_ALLOW: - case constants.USER_BLOCK: - case constants.USER_COOKIE_BLOCK: - return 5; + case constants.NO_TRACKING: + return 0; + case constants.ALLOW: + return 1; + case constants.BLOCK: + return 2; + case constants.COOKIEBLOCK: + return 3; + case constants.DNT: + return 4; + case constants.USER_ALLOW: + case constants.USER_BLOCK: + case constants.USER_COOKIE_BLOCK: + return 5; } } diff --git a/src/js/webrequest.js b/src/js/webrequest.js index 979a45e3a5..1fe1de920d 100644 --- a/src/js/webrequest.js +++ b/src/js/webrequest.js @@ -615,12 +615,40 @@ function unblockWidgetOnTab(tabId, widgetUrls) { // NOTE: sender.tab is available for content script (not popup) messages only function dispatcher(request, sender, sendResponse) { - if (request.checkEnabled) { + + // messages from content scripts are to be treated with greater caution: + // https://groups.google.com/a/chromium.org/d/msg/chromium-extensions/0ei-UCHNm34/lDaXwQhzBAAJ + if (!sender.url.startsWith(chrome.runtime.getURL(""))) { + // reject unless it's a known content script message + const KNOWN_CONTENT_SCRIPT_MESSAGES = [ + "checkDNT", + "checkEnabled", + "checkEnabledAndThirdParty", + "checkLocation", + "checkReplaceButton", + "checkWidgetReplacementEnabled", + "fpReport", + "getReplacementButton", + "supercookieReport", + "unblockWidget", + ]; + if (!KNOWN_CONTENT_SCRIPT_MESSAGES.includes(request.type)) { + console.error("Rejected unknown message %o from %s", request, sender.url); + return sendResponse(); + } + } + + switch (request.type) { + + case "checkEnabled": { sendResponse(badger.isPrivacyBadgerEnabled( window.extractHostFromURL(sender.tab.url) )); - } else if (request.checkLocation) { + break; + } + + case "checkLocation": { if (!badger.isPrivacyBadgerEnabled(window.extractHostFromURL(sender.tab.url))) { return sendResponse(); } @@ -630,31 +658,37 @@ function dispatcher(request, sender, sendResponse) { return sendResponse(); } - let requestHost = window.extractHostFromURL(request.checkLocation); + let requestHost = window.extractHostFromURL(request.frameUrl); // Ignore requests that aren't from a third party. if (!utils.isThirdPartyDomain(requestHost, window.extractHostFromURL(sender.tab.url))) { return sendResponse(); } - var reqAction = checkAction(sender.tab.id, requestHost); - var cookieBlock = reqAction == constants.COOKIEBLOCK || reqAction == constants.USER_COOKIE_BLOCK; - sendResponse(cookieBlock); + let action = checkAction(sender.tab.id, requestHost); + sendResponse(action == constants.COOKIEBLOCK || action == constants.USER_COOKIE_BLOCK); + + break; + } - } else if (request.checkReplaceButton) { + case "checkReplaceButton": { if (badger.isPrivacyBadgerEnabled(window.extractHostFromURL(sender.tab.url)) && badger.isWidgetReplacementEnabled()) { let widgetBlockList = getWidgetBlockList(); sendResponse(widgetBlockList); } - } else if (request.unblockWidget) { + break; + } + + case "unblockWidget": { unblockWidgetOnTab(sender.tab.id, request.buttonUrls); sendResponse(); + break; + } - } else if (request.getReplacementButton) { - + case "getReplacementButton": { let button_path = chrome.runtime.getURL( - "skin/socialwidgets/" + request.getReplacementButton); + "skin/socialwidgets/" + request.imagePath); let image_type = button_path.slice(button_path.lastIndexOf('.') + 1); @@ -679,36 +713,48 @@ function dispatcher(request, sender, sendResponse) { // indicate this is an async response to chrome.runtime.onMessage return true; + } - // Canvas fingerprinting - } else if (request.fpReport) { - if (!badger.isPrivacyBadgerEnabled(window.extractHostFromURL(sender.tab.url))) { return; } - if (Array.isArray(request.fpReport)) { - request.fpReport.forEach(function (msg) { + case "fpReport": { + if (!badger.isPrivacyBadgerEnabled(window.extractHostFromURL(sender.tab.url))) { + return sendResponse(); + } + if (Array.isArray(request.data)) { + request.data.forEach(function (msg) { recordFingerprinting(sender.tab.id, msg); }); } else { - recordFingerprinting(sender.tab.id, request.fpReport); + recordFingerprinting(sender.tab.id, request.data); } - } else if (request.superCookieReport) { - if (badger.hasSuperCookie(request.superCookieReport)) { + break; + } + + case "supercookieReport": { + if (badger.hasSuperCookie(request.data)) { recordSuperCookie(sender.tab.id, request.frameUrl); } + break; + } - } else if (request.checkEnabledAndThirdParty) { + case "checkEnabledAndThirdParty": { let tab_host = window.extractHostFromURL(sender.tab.url), - frame_host = window.extractHostFromURL(request.checkEnabledAndThirdParty); + frame_host = window.extractHostFromURL(request.frameUrl); sendResponse(badger.isPrivacyBadgerEnabled(tab_host) && utils.isThirdPartyDomain(frame_host, tab_host)); - } else if (request.checkWidgetReplacementEnabled) { + break; + } + + case "checkWidgetReplacementEnabled": { sendResponse( badger.isPrivacyBadgerEnabled(window.extractHostFromURL(sender.tab.url)) && badger.isWidgetReplacementEnabled() ); + break; + } - } else if (request.type == "getPopupData") { + case "getPopupData": { let tab_id = request.tabId, tab_url = request.tabUrl, tab_host = window.extractHostFromURL(tab_url), @@ -728,7 +774,10 @@ function dispatcher(request, sender, sendResponse) { trackerCount: has_tab_data && badger.getTrackerCount(tab_id) }); - } else if (request.type == "getOptionsData") { + break; + } + + case "getOptionsData": { sendResponse({ disabledSites: badger.getDisabledSites(), isCheckingDNTPolicyEnabled: badger.isCheckingDNTPolicyEnabled(), @@ -742,35 +791,50 @@ function dispatcher(request, sender, sendResponse) { webRTCAvailable: badger.webRTCAvailable, }); - } else if (request.type == "resetData") { + break; + } + + case "resetData": { badger.storage.clearTrackerData(); badger.loadSeedData(); sendResponse(); + break; + } - } else if (request.type == "removeAllData") { + case "removeAllData": { badger.storage.clearTrackerData(); sendResponse(); + break; + } - } else if (request.type == "seenComic") { + case "seenComic": { badger.getSettings().setItem("seenComic", true); + break; + } - } else if (request.type == "activateOnSite") { + case "activateOnSite": { badger.enablePrivacyBadgerForOrigin(request.tabHost); badger.refreshIconAndContextMenu(request.tabId, request.tabUrl); sendResponse(); + break; + } - } else if (request.type == "deactivateOnSite") { + case "deactivateOnSite": { badger.disablePrivacyBadgerForOrigin(request.tabHost); badger.refreshIconAndContextMenu(request.tabId, request.tabUrl); sendResponse(); + break; + } - } else if (request.type == "revertDomainControl") { + case "revertDomainControl": { badger.storage.revertUserAction(request.origin); sendResponse({ origins: badger.storage.getTrackingDomains() }); + break; + } - } else if (request.type == "downloadCloud") { + case "downloadCloud": { chrome.storage.sync.get("disabledSites", function (store) { if (chrome.runtime.lastError) { sendResponse({success: false, message: chrome.runtime.lastError.message}); @@ -791,10 +855,12 @@ function dispatcher(request, sender, sendResponse) { }); } }); - //indicate this is an async response to chrome.runtime.onMessage + + // indicate this is an async response to chrome.runtime.onMessage return true; + } - } else if (request.type == "uploadCloud") { + case "uploadCloud": { let obj = {}; obj.disabledSites = badger.getDisabledSites(); chrome.storage.sync.set(obj, function () { @@ -804,10 +870,11 @@ function dispatcher(request, sender, sendResponse) { sendResponse({success: true}); } }); - //indicate this is an async response to chrome.runtime.onMessage + // indicate this is an async response to chrome.runtime.onMessage return true; + } - } else if (request.type == "savePopupToggle") { + case "savePopupToggle": { let domain = request.origin, action = request.action; @@ -816,22 +883,29 @@ function dispatcher(request, sender, sendResponse) { // update cached tab data so that a reopened popup displays correct state badger.tabData[request.tabId].origins[domain] = "user_" + action; - } else if (request.type == "saveOptionsToggle") { + break; + } + + case "saveOptionsToggle": { // called when the user manually sets a slider on the options page badger.saveAction(request.action, request.origin); sendResponse({ origins: badger.storage.getTrackingDomains() }); + break; + } - } else if (request.type == "mergeUserData") { + case "mergeUserData": { // called when a user uploads data exported from another Badger instance badger.mergeUserData(request.data); sendResponse({ disabledSites: badger.getDisabledSites(), origins: badger.storage.getTrackingDomains(), }); + break; + } - } else if (request.type == "updateSettings") { + case "updateSettings": { const settings = badger.getSettings(); for (let key in request.data) { if (badger.defaultSettings.hasOwnProperty(key)) { @@ -841,42 +915,56 @@ function dispatcher(request, sender, sendResponse) { } } sendResponse(); + break; + } - } else if (request.type == "updateBadge") { + case "updateBadge": { let tab_id = request.tab_id; badger.updateBadge(tab_id); sendResponse(); + break; + } - } else if (request.type == "disablePrivacyBadgerForOrigin") { + case "disablePrivacyBadgerForOrigin": { badger.disablePrivacyBadgerForOrigin(request.domain); sendResponse({ disabledSites: badger.getDisabledSites() }); + break; + } - } else if (request.type == "enablePrivacyBadgerForOriginList") { + case "enablePrivacyBadgerForOriginList": { request.domains.forEach(function (domain) { badger.enablePrivacyBadgerForOrigin(domain); }); sendResponse({ disabledSites: badger.getDisabledSites() }); + break; + } - } else if (request.type == "removeOrigin") { + case "removeOrigin": { badger.storage.getBadgerStorageObject("snitch_map").deleteItem(request.origin); badger.storage.getBadgerStorageObject("action_map").deleteItem(request.origin); sendResponse({ origins: badger.storage.getTrackingDomains() }); + break; + } - } else if (request.type == "saveErrorText") { + case "saveErrorText": { let activeTab = badger.tabData[request.tabId]; activeTab.errorText = request.errorText; + break; + } - } else if (request.type == "removeErrorText") { + case "removeErrorText": { let activeTab = badger.tabData[request.tabId]; delete activeTab.errorText; + break; + } - } else if (request.checkDNT) { + case "checkDNT": { // called from contentscripts/dnt.js to check if we should enable it sendResponse( badger.isDNTSignalEnabled() @@ -884,6 +972,9 @@ function dispatcher(request, sender, sendResponse) { window.extractHostFromURL(sender.tab.url) ) ); + break; + } + } } diff --git a/src/tests/tests/firstparties.js b/src/tests/tests/firstparties.js index 17e185260c..c16cab711c 100644 --- a/src/tests/tests/firstparties.js +++ b/src/tests/tests/firstparties.js @@ -48,7 +48,7 @@ function stub(elts, selector) { // Stub runtime.sendMessage so that it returns `true` in response to the // `checkEnabled` query. chrome.runtime.sendMessage = function (message, callback) { - if (message.checkEnabled) { + if (message.type == "checkEnabled") { callback(true); } else { chrome.runtime.sendMessageBefore(message, callback);