diff options
Diffstat (limited to 'data/extensions/https-everywhere@eff.org/background-scripts')
11 files changed, 0 insertions, 2839 deletions
diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/background.js b/data/extensions/https-everywhere@eff.org/background-scripts/background.js deleted file mode 100644 index 78a9aca..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/background.js +++ /dev/null @@ -1,959 +0,0 @@ -"use strict"; - -(function(exports) { - -const rules = require('./rules'), - store = require('./store'), - incognito = require('./incognito'), - util = require('./util'), - update = require('./update'), - { update_channels } = require('./update_channels'), - wasm = require('./wasm'), - ipUtils = require('./ip_utils'), - ssl_codes = require('./ssl_codes'); - -let all_rules = new rules.RuleSets(); -let blooms = []; - -async function initialize() { - await wasm.initialize(); - await store.initialize(); - await store.performMigrations(); - await initializeStoredGlobals(); - await getUpgradeToSecureAvailable(); - await update.initialize(store, initializeAllRules); - await all_rules.loadFromBrowserStorage(store, update.applyStoredRulesets); - await update.applyStoredBlooms(blooms); - await incognito.onIncognitoDestruction(destroy_caches); -} -initialize(); - -async function initializeAllRules() { - const r = new rules.RuleSets(); - await r.loadFromBrowserStorage(store, update.applyStoredRulesets); - Object.assign(all_rules, r); - blooms.length = 0; - await update.applyStoredBlooms(blooms); -} - -/** - * Load preferences. Structure is: - * { - * httpNowhere: Boolean, - * isExtensionEnabled: Boolean - * } - */ -var httpNowhereOn = false; -var isExtensionEnabled = true; -let disabledList = new Set(); -let httpOnceList = new Set(); - -/** - * Check if HTTPS Everywhere should be ON for host - */ -function isExtensionDisabledOnSite(host) { - // make sure the host is not matched in the httpOnceList - if (httpOnceList.has(host)) { - return true; - } - - // make sure the host is not matched in the disabledList - if (disabledList.has(host)) { - return true; - } - - // make sure the host is matched by any wildcard expressions in the disabledList - const experessions = util.getWildcardExpressions(host); - for (const expression of experessions) { - if (disabledList.has(expression)) { - return true; - } - } - - // otherwise return false - return false; -} - -function initializeStoredGlobals() { - return new Promise(resolve => { - store.get({ - httpNowhere: false, - globalEnabled: true, - enableMixedRulesets: false, - disabledList: [] - }, function(item) { - httpNowhereOn = item.httpNowhere; - isExtensionEnabled = item.globalEnabled; - for (let disabledSite of item.disabledList) { - disabledList.add(disabledSite); - } - updateState(); - - rules.settings.enableMixedRulesets = item.enableMixedRulesets; - - resolve(); - }); - }); -} - -/** @type {boolean} */ -let upgradeToSecureAvailable = false; - -function getUpgradeToSecureAvailable() { - if (typeof browser !== 'undefined') { - return browser.runtime.getBrowserInfo().then(function(info) { - let version = info.version.match(/^(\d+)/)[1]; - if (info.name == "Firefox" && version >= 59) { - upgradeToSecureAvailable = true; - } else { - upgradeToSecureAvailable = false; - } - }); - } else { - return new Promise(resolve => { - upgradeToSecureAvailable = false; - resolve(); - }); - } -} - -chrome.storage.onChanged.addListener(async function(changes, areaName) { - if (areaName === 'sync' || areaName === 'local') { - if ('httpNowhere' in changes) { - httpNowhereOn = changes.httpNowhere.newValue; - updateState(); - } - if ('globalEnabled' in changes) { - isExtensionEnabled = changes.globalEnabled.newValue; - updateState(); - } - if ('enableMixedRulesets' in changes) { - // Don't require users to restart the browsers - rules.settings.enableMixedRulesets = changes.enableMixedRulesets.newValue; - initializeAllRules(); - } - if ('debugging_rulesets' in changes) { - initializeAllRules(); - } - } -}); - -if (chrome.tabs) { - chrome.tabs.onActivated.addListener(function() { - updateState(); - }); -} -if (chrome.windows) { - chrome.windows.onFocusChanged.addListener(function() { - updateState(); - }); - - // Grant access to HTTP site only during session, clear once window is closed - chrome.windows.onRemoved.addListener(function() { - chrome.windows.getAll({}, function(windows) { - if(windows.length > 0) { - return; - } else { - httpOnceList.clear(); - } - }); - }); - -} -chrome.webNavigation.onCompleted.addListener(function() { - updateState(); -}); - -/** - * Set the icon color correctly - * active: extension is enabled. - * blocking: extension is in "block all HTTP requests" mode. - * disabled: extension is disabled from the popup menu. - */ - -function updateState () { - if (!chrome.tabs) return; - - let iconState = 'active'; - - if (!isExtensionEnabled) { - iconState = 'disabled'; - } else if (httpNowhereOn) { - iconState = 'blocking'; - } - - chrome.browserAction.setTitle({ - title: 'HTTPS Everywhere' + ((iconState === 'active') ? '' : ' (' + iconState + ')') - }); - - const chromeUrl = 'chrome://'; - - chrome.tabs.query({ active: true, currentWindow: true, status: 'complete' }, function(tabs) { - if (!tabs || tabs.length === 0 || tabs[0].url.startsWith(chromeUrl) ) { - return; - } - - // tabUrl.host instead of hostname should be used to show the "disabled" status properly (#19293) - const tabUrl = new URL(tabs[0].url); - const host = util.getNormalisedHostname(tabUrl.host); - - if (isExtensionDisabledOnSite(host) || iconState == "disabled") { - if ('setIcon' in chrome.browserAction) { - chrome.browserAction.setIcon({ - path: { - 38: 'images/icons/icon-disabled-38.png' - } - }); - } - } else { - if ('setIcon' in chrome.browserAction) { - chrome.browserAction.setIcon({ - path: { - 38: 'images/icons/icon-' + iconState + '-38.png' - } - }); - } - } - }); -} - -/** - * The following allows fennec to interact with the popup ui - * */ -chrome.browserAction.onClicked.addListener(e => { - const url = chrome.runtime.getURL("/pages/popup/index.html?tabId=" + e.id); - chrome.tabs.create({ - url - }); -}); - -/** - * A centralized storage for browsing data within the browser session. - */ -function BrowserSession() { - this.tabs = new Map(); - this.requests = new Map(); - - if (chrome.tabs) { - chrome.tabs.onRemoved.addListener(tabId => { - this.deleteTab(tabId); - }); - } -} - -BrowserSession.prototype = { - putTab: function(tabId, key, value, overwrite) { - if (!this.tabs.has(tabId)) { - this.tabs.set(tabId, {}); - } - - if (!(key in this.tabs.get(tabId)) || overwrite) { - this.tabs.get(tabId)[key] = value; - } - }, - - getTab: function(tabId, key, defaultValue) { - if (this.tabs.has(tabId) && key in this.tabs.get(tabId)) { - return this.tabs.get(tabId)[key]; - } - return defaultValue; - }, - - deleteTab: function(tabId) { - if (this.tabs.has(tabId)) { - this.tabs.delete(tabId); - } - }, - - putTabAppliedRulesets: function(tabId, type, ruleset) { - this.putTab(tabId, "main_frame", false, false); - - // always show main_frame ruleset on the top - if (type == "main_frame") { - this.putTab(tabId, "main_frame", true, true); - this.putTab(tabId, "applied_rulesets", [ruleset,], true); - return ; - } - - // sort by ruleset names alphabetically, case-insensitive - if (this.getTab(tabId, "applied_rulesets", null)) { - let rulesets = this.getTab(tabId, "applied_rulesets", null); - let insertIndex = 0; - - const ruleset_name = ruleset.name.toLowerCase(); - - for (const item of rulesets) { - const item_name = item.name.toLowerCase(); - - if (item_name == ruleset_name) { - return ; - } else if (insertIndex == 0 && this.getTab(tabId, "main_frame", false)) { - insertIndex = 1; - } else if (item_name < ruleset_name) { - insertIndex++; - } - } - rulesets.splice(insertIndex, 0, ruleset); - } else { - this.putTab(tabId, "applied_rulesets", [ruleset,], true); - } - }, - - getTabAppliedRulesets: function(tabId) { - return this.getTab(tabId, "applied_rulesets", null); - }, - - putRequest: function(requestId, key, value) { - if (!this.requests.has(requestId)) { - this.requests.set(requestId, {}); - } - this.requests.get(requestId)[key] = value; - }, - - getRequest: function(requestId, key, defaultValue) { - if (this.requests.has(requestId) && key in this.requests.get(requestId)) { - return this.requests.get(requestId)[key]; - } - return defaultValue; - }, - - deleteRequest: function(requestId) { - if (this.requests.has(requestId)) { - this.requests.delete(requestId); - } - } -}; - -let browserSession = new BrowserSession(); - -var urlBlacklist = new Set(); - -const cancelUrl = chrome.runtime.getURL("/pages/cancel/index.html"); - -function redirectOnCancel(shouldCancel, originURL) { - return shouldCancel ? {redirectUrl: newCancelUrl(originURL)} : {cancel: false}; -} - -const newCancelUrl = originURL => `${cancelUrl}?originURL=${encodeURIComponent(originURL)}`; - -/** - * Called before a HTTP(s) request. Does the heavy lifting - * Cancels the request/redirects it to HTTPS. URL modification happens in here. - * @param details of the handler, see Chrome doc - * */ -function onBeforeRequest(details) { - // If HTTPSe has been disabled by the user, return immediately. - if (!isExtensionEnabled) { - return; - } - - let uri = new URL(details.url); - - // Normalise hosts with tailing dots, e.g. "www.example.com." - uri.hostname = util.getNormalisedHostname(uri.hostname); - - let ip = ipUtils.parseIp(uri.hostname); - - let isLocalIp = false; - - if (ip !== -1) { - isLocalIp = ipUtils.isLocalIp(ip); - } - - if (details.type == "main_frame") { - // Clear the content from previous browser session. - // This needed to be done before this listener returns, - // otherwise, the extension popup might include rulesets - // from previous page. - browserSession.deleteTab(details.tabId); - - // Check if an user has disabled HTTPS Everywhere on this site. We should - // ensure that all subresources are not run through HTTPS Everywhere as well. - browserSession.putTab(details.tabId, 'first_party_host', uri.host, true); - } - - if (isExtensionDisabledOnSite(browserSession.getTab(details.tabId, 'first_party_host', null))) { - return; - } - - // Should the request be canceled? - // true if the URL is a http:// connection to a remote canonical host, and not - // a tor hidden service - const shouldCancel = httpNowhereOn && - (uri.protocol === 'http:' || uri.protocol === 'ftp:') && - uri.hostname.slice(-6) !== '.onion' && - uri.hostname !== 'localhost' && - !uri.hostname.endsWith('.localhost') && - uri.hostname !== '[::1]' && - !isLocalIp; - - // If there is a username / password, put them aside during the ruleset - // analysis process - let using_credentials_in_url = false; - let tmp_user; - let tmp_pass; - if (uri.password || uri.username) { - using_credentials_in_url = true; - tmp_user = uri.username; - tmp_pass = uri.password; - uri.username = ''; - uri.password = ''; - } - - if (details.url != uri.href && !using_credentials_in_url) { - util.log(util.INFO, "Original url " + details.url + - " changed before processing to " + uri.href); - } - if (urlBlacklist.has(uri.href)) { - return redirectOnCancel(shouldCancel, details.url); - } - - if (browserSession.getRequest(details.requestId, "redirect_count", 0) >= 8) { - util.log(util.NOTE, "Redirect counter hit for " + uri.href); - urlBlacklist.add(uri.href); - rules.settings.domainBlacklist.add(uri.hostname); - util.log(util.WARN, "Domain blacklisted " + uri.hostname); - return redirectOnCancel(shouldCancel, details.url); - } - - // whether to use mozilla's upgradeToSecure BlockingResponse if available - let upgradeToSecure = false; - let newuristr = null; - - let potentiallyApplicable = all_rules.potentiallyApplicableRulesets(uri.hostname); - - for (let ruleset of potentiallyApplicable) { - if (details.url.match(ruleset.scope)) { - browserSession.putTabAppliedRulesets(details.tabId, details.type, ruleset); - if (ruleset.active && !newuristr) { - newuristr = ruleset.apply(uri.href); - } - } - } - - if (newuristr == null && blooms.length > 0 && uri.protocol === 'http:') { - for(let bloom of blooms) { - if(bloom.check(uri.hostname)) { - newuristr = uri.href.replace(/^http:/, "https:"); - break; - } - } - } - - // only use upgradeToSecure for trivial rewrites - if (upgradeToSecureAvailable && newuristr) { - // check rewritten URIs against the trivially upgraded URI - const trivialUpgradeUri = uri.href.replace(/^http:/, "https:"); - upgradeToSecure = (newuristr == trivialUpgradeUri); - } - - // re-insert userpass info which was stripped temporarily - if (using_credentials_in_url) { - if (newuristr) { - const uri_with_credentials = new URL(newuristr); - uri_with_credentials.username = tmp_user; - uri_with_credentials.password = tmp_pass; - newuristr = uri_with_credentials.href; - } else { - const url_with_credentials = new URL(uri.href); - url_with_credentials.username = tmp_user; - url_with_credentials.password = tmp_pass; - uri.href = url_with_credentials.href; - } - } - - if (httpNowhereOn) { - // If loading a main frame, try the HTTPS version as an alternative to - // failing. - if (shouldCancel) { - if (!newuristr) { - newuristr = uri.href.replace(/^http:/, "https:"); - browserSession.putRequest(details.requestId, "simple_http_nowhere_redirect", true); - upgradeToSecure = true; - } else { - newuristr = newuristr.replace(/^http:/, "https:"); - } - } - if ( - newuristr && - ( - newuristr.substring(0, 5) === "http:" || - newuristr.substring(0, 4) === "ftp:" - ) - ) { - // Abort early if we're about to redirect to HTTP or FTP in HTTP Nowhere mode - return {redirectUrl: newCancelUrl(newuristr)}; - } - } - - if (upgradeToSecureAvailable && upgradeToSecure) { - util.log(util.INFO, 'onBeforeRequest returning upgradeToSecure: true'); - return {upgradeToSecure: true}; - } else if (newuristr) { - util.log(util.INFO, 'onBeforeRequest returning redirectUrl: ' + newuristr); - return {redirectUrl: newuristr}; - } else { - util.log(util.INFO, 'onBeforeRequest returning shouldCancel: ' + shouldCancel); - return redirectOnCancel(shouldCancel, details.url); - } -} - -/** - * monitor cookie changes. Automatically convert them to secure cookies - * @param changeInfo Cookie changed info, see Chrome doc - * */ -function onCookieChanged(changeInfo) { - if (!changeInfo.removed && !changeInfo.cookie.secure && isExtensionEnabled) { - if (all_rules.shouldSecureCookie(changeInfo.cookie)) { - let cookie = { - name:changeInfo.cookie.name, - value:changeInfo.cookie.value, - path:changeInfo.cookie.path, - httpOnly:changeInfo.cookie.httpOnly, - expirationDate:changeInfo.cookie.expirationDate, - storeId:changeInfo.cookie.storeId, - secure: true - }; - - // Host-only cookies don't set the domain field. - if (!changeInfo.cookie.hostOnly) { - cookie.domain = changeInfo.cookie.domain; - } - - // Chromium cookie sameSite status, see https://tools.ietf.org/html/draft-west-first-party-cookies - if (changeInfo.cookie.sameSite) { - cookie.sameSite = changeInfo.cookie.sameSite; - } - - // Firefox first-party isolation - if (changeInfo.cookie.firstPartyDomain) { - cookie.firstPartyDomain = changeInfo.cookie.firstPartyDomain; - } - - // The cookie API is magical -- we must recreate the URL from the domain and path. - if (changeInfo.cookie.domain[0] == ".") { - cookie.url = "https://www" + changeInfo.cookie.domain + cookie.path; - } else { - cookie.url = "https://" + changeInfo.cookie.domain + cookie.path; - } - // We get repeated events for some cookies because sites change their - // value repeatedly and remove the "secure" flag. - util.log(util.DBUG, - "Securing cookie " + cookie.name + " for " + changeInfo.cookie.domain + ", was secure=" + changeInfo.cookie.secure); - chrome.cookies.set(cookie); - } - } -} - -/** - * handling redirects, breaking loops - * @param details details for the redirect (see chrome doc) - * */ -function onBeforeRedirect(details) { - // Catch redirect loops (ignoring about:blank, etc. caused by other extensions) - let prefix = details.redirectUrl.substring(0, 5); - if (prefix === "http:" || prefix === "https") { - let count = browserSession.getRequest(details.requestId, "redirect_count", 0); - if (count) { - browserSession.putRequest(details.requestId, "redirect_count", count + 1); - util.log(util.DBUG, "Got redirect id " + details.requestId + ": "+count); - } else { - browserSession.putRequest(details.requestId, "redirect_count", 1); - } - } -} - -/** - * handle webrequest.onCompleted, cleanup redirectCounter - * @param details details for the chrome.webRequest (see chrome doc) - */ -function onCompleted(details) { - browserSession.deleteRequest(details.requestId); -} - -/** - * handle webrequest.onErrorOccurred, cleanup redirectCounter - * @param details details for the chrome.webRequest (see chrome doc) - */ -function onErrorOccurred(details) { - if (httpNowhereOn && - details.type == "main_frame" && - browserSession.getRequest(details.requestId, "simple_http_nowhere_redirect", false) && - // Enumerate errors that are likely due to HTTPS misconfigurations - ssl_codes.error_list.some(message => details.error.includes(message)) - ) { - let url = new URL(details.url); - if (url.protocol == "https:") { - url.protocol = "http:"; - } - chrome.tabs.update(details.tabId, {url: newCancelUrl(url.toString())}); - } - - browserSession.deleteRequest(details.requestId); -} - -/** - * handle webrequest.onHeadersReceived, insert upgrade-insecure-requests directive and - * rewrite access-control-allow-origin if presented in HTTP Nowhere mode - * @param details details for the chrome.webRequest (see chrome doc) - */ -function onHeadersReceived(details) { - if (isExtensionEnabled && httpNowhereOn) { - // Do not upgrade the .onion requests in EASE mode, - // See https://github.com/EFForg/https-everywhere/pull/14600#discussion_r168072480 - const uri = new URL(details.url); - const hostname = util.getNormalisedHostname(uri.hostname); - if (hostname.slice(-6) == '.onion') { - return {}; - } - - // Do not upgrade resources if the first-party domain disbled EASE mode - // This is needed for HTTPS sites serve mixed content and is broken - if (isExtensionDisabledOnSite(browserSession.getTab(details.tabId, 'first_party_host', null))) { - return {}; - } - - let responseHeadersChanged = false; - let cspHeaderFound = false; - - for (const idx in details.responseHeaders) { - if (details.responseHeaders[idx].name.match(/Content-Security-Policy/i)) { - // Existing CSP headers found - cspHeaderFound = true; - const value = details.responseHeaders[idx].value; - - // Prepend if no upgrade-insecure-requests directive exists - if (!value.match(/upgrade-insecure-requests/i)) { - details.responseHeaders[idx].value = "upgrade-insecure-requests; " + value; - responseHeadersChanged = true; - } - } - - if (details.responseHeaders[idx].name.match(/Access-Control-Allow-Origin/i)) { - // Existing access-control-allow-origin header found - const value = details.responseHeaders[idx].value; - - // If HTTP protocol is used, change it to HTTPS - if (value.match(/http:/)) { - details.responseHeaders[idx].value = value.replace(/http:/g, "https:"); - responseHeadersChanged = true; - } - } - } - - if (!cspHeaderFound) { - // CSP headers not found - const upgradeInsecureRequests = { - name: 'Content-Security-Policy', - value: 'upgrade-insecure-requests' - }; - details.responseHeaders.push(upgradeInsecureRequests); - responseHeadersChanged = true; - } - - if (responseHeadersChanged) { - return {responseHeaders: details.responseHeaders}; - } - } - return {}; -} - -// Registers the handler for requests -// See: https://github.com/EFForg/https-everywhere/issues/10039 -chrome.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: ["*://*/*", "ftp://*/*"]}, ["blocking"]); - -// Try to catch redirect loops on URLs we've redirected to HTTPS. -chrome.webRequest.onBeforeRedirect.addListener(onBeforeRedirect, {urls: ["https://*/*"]}); - -// Cleanup redirectCounter if necessary -chrome.webRequest.onCompleted.addListener(onCompleted, {urls: ["*://*/*"]}); - -// Cleanup redirectCounter if necessary -chrome.webRequest.onErrorOccurred.addListener(onErrorOccurred, {urls: ["*://*/*"]}); - -// Insert upgrade-insecure-requests directive in httpNowhere mode -chrome.webRequest.onHeadersReceived.addListener(onHeadersReceived, {urls: ["https://*/*"]}, ["blocking", "responseHeaders"]); - -// Listen for cookies set/updated and secure them if applicable. This function is async/nonblocking. -chrome.cookies.onChanged.addListener(onCookieChanged); - -// This is necessary for communication with the popup in Firefox Private -// Browsing Mode, see https://bugzilla.mozilla.org/show_bug.cgi?id=1329304 -chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { - - function get_update_channels_generic(update_channels) { - let last_updated_promises = []; - for(let update_channel of update_channels) { - last_updated_promises.push(new Promise(resolve => { - store.local.get({['uc-timestamp: ' + update_channel.name]: 0}, item => { - resolve([update_channel.name, item['uc-timestamp: ' + update_channel.name]]); - }); - })); - } - Promise.all(last_updated_promises).then(results => { - const last_updated = results.reduce((obj, item) => { - obj[item[0]] = item[1]; - return obj; - }, {}); - sendResponse({update_channels, last_updated}); - }); - } - - function storeDisabledList(message) { - - const disabledListArray = Array.from(disabledList); - const httpOnceListArray = Array.from(httpOnceList); - - if (message === 'once') { - store.set({httpOnceList: httpOnceListArray}, () => { - sendResponse(true); - }); - } else { - store.set({disabledList: disabledListArray}, () => { - sendResponse(true); - }); - } - - return true; - } - - const responses = { - get_option: () => { - store.get(message.object, sendResponse); - return true; - }, - set_option: () => { - store.set(message.object, item => { - if (sendResponse) { - sendResponse(item); - } - }); - }, - delete_from_ruleset_cache: () => { - all_rules.ruleCache.delete(message.object); - }, - get_applied_rulesets: () => { - sendResponse(browserSession.getTabAppliedRulesets(message.object)); - return true; - }, - set_ruleset_active_status: () => { - let rulesets = browserSession.getTabAppliedRulesets(message.object.tab_id); - - for (let ruleset of rulesets) { - if (ruleset.name == message.object.name) { - ruleset.active = message.object.active; - if (ruleset.default_state == message.object.active) { - message.object.active = undefined; - } - break; - } - } - - all_rules.setRuleActiveState(message.object.name, message.object.active).then(() => { - sendResponse(true); - }); - - return true; - }, - reset_to_defaults: () => { - // restore the 'default states' of the rulesets - store.set_promise('ruleActiveStates', {}).then(() => { - // clear the caches such that it becomes stateless - destroy_caches(); - // re-activate all rules according to the new states - initializeAllRules(); - // reload tabs when operations completed - chrome.tabs.reload(); - }); - }, - get_user_rules: () => { - store.get_promise(all_rules.USER_RULE_KEY, []).then(userRules => sendResponse(userRules)); - return true; - }, - add_new_rule: () => { - all_rules.addNewRuleAndStore(message.object).then(() => { - sendResponse(true); - }); - return true; - }, - remove_rule: () => { - all_rules.removeRuleAndStore(message.object.ruleset, message.object.src) - .then(() => { - /** - * FIXME: initializeAllRules is needed for calls from the option pages. - * Since message.object is not of type Ruleset, rules.removeUserRule - * is not usable... - */ - if (message.object.src === 'options') { - return initializeAllRules(); - } - }) - .then(() => { - if (sendResponse !== null) { - sendResponse(true); - } - }); - return true; - }, - get_update_channel_timestamps: () => { - update.getUpdateChannelTimestamps().then(timestamps => sendResponse(timestamps)); - return true; - }, - get_pinned_update_channels: () => { - get_update_channels_generic(update_channels); - return true; - }, - get_stored_update_channels: () => { - store.get({update_channels: []}, item => { - get_update_channels_generic(item.update_channels); - }); - return true; - }, - create_update_channel: () => { - - store.get({update_channels: []}, item => { - - const update_channel_names = update_channels.concat(item.update_channels).reduce((obj, item) => { - obj.add(item.name); - return obj; - }, new Set()); - - if(update_channel_names.has(message.object)) { - return sendResponse(false); - } - - item.update_channels.push({ - name: message.object, - jwk: {}, - update_path_prefix: '', - scope: '' - }); - - store.set({update_channels: item.update_channels}, () => { - sendResponse(true); - }); - - }); - return true; - }, - delete_update_channel: () => { - store.get({update_channels: []}, item => { - store.set({update_channels: item.update_channels.filter(update_channel => { - return (update_channel.name != message.object); - })}, () => { - store.local.remove([ - 'uc-timestamp: ' + message.object, - 'uc-stored-timestamp: ' + message.object, - 'rulesets: ' + message.object, - 'bloom: ' + message.object, - 'bloom_bitmap_bits: ' + message.object, - 'bloom_k_num: ' + message.object, - 'bloom_sip_keys_0_0: ' + message.object, - 'bloom_sip_keys_0_1: ' + message.object, - 'bloom_sip_keys_1_0: ' + message.object, - 'bloom_sip_keys_1_1: ' + message.object, - ], () => { - initializeAllRules(); - sendResponse(true); - }); - }); - }); - return true; - }, - update_update_channel: () => { - store.get({update_channels: []}, item => { - let scope_changed = false; - item.update_channels = item.update_channels.map(update_channel => { - if(update_channel.name == message.object.name) { - if(update_channel.scope != message.object.scope) { - scope_changed = true; - } - update_channel = message.object; - } - return update_channel; - }); - - // Ensure that we check for new rulesets from the update channel immediately. - // If the scope has changed, make sure that the rulesets are re-initialized. - update.removeStorageListener(); - store.set({update_channels: item.update_channels}, () => { - update.loadUpdateChannelsKeys().then(() => { - update.resetTimer(); - if(scope_changed) { - initializeAllRules(); - } - sendResponse(true); - }); - update.addStorageListener(); - }); - }); - return true; - }, - get_simple_rules_ending_with: () => { - return sendResponse(all_rules.getSimpleRulesEndingWith(message.object)); - }, - get_last_checked: () => { - store.local.get({'last-checked': false}, item => { - sendResponse(item['last-checked']); - }); - return true; - }, - disable_on_site_once: () => { - httpOnceList.add(message.object); - return storeDisabledList('once'); - }, - disable_on_site: () => { - const host = util.getNormalisedHostname(message.object); - // always validate hostname before adding it to the disabled list - if (util.isValidHostname(host)) { - disabledList.add(host); - return storeDisabledList('disable'); - } - return sendResponse(false); - }, - enable_on_site: () => { - disabledList.delete(util.getNormalisedHostname(message.object)); - return storeDisabledList('enable'); - }, - check_if_site_disabled: () => { - return sendResponse(isExtensionDisabledOnSite(util.getNormalisedHostname(message.object))); - }, - is_firefox: () => { - if(typeof(browser) != "undefined") { - browser.runtime.getBrowserInfo().then(function(info) { - if (info.name == "Firefox") { - sendResponse(true); - } else { - sendResponse(false); - } - }); - } else { - sendResponse(false); - } - return true; - } - }; - if (message.type in responses) { - return responses[message.type](); - } -}); - -/** - * Clear any cache/ blacklist we have. - */ -function destroy_caches() { - util.log(util.DBUG, "Destroying caches."); - all_rules.cookieHostCache.clear(); - all_rules.ruleCache.clear(); - rules.settings.domainBlacklist.clear(); - urlBlacklist.clear(); - httpOnceList.clear(); -} - -Object.assign(exports, { - all_rules, - blooms, - urlBlacklist -}); - -})(typeof exports == 'undefined' ? require.scopes.background = {} : exports); diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/bootstrap.js b/data/extensions/https-everywhere@eff.org/background-scripts/bootstrap.js deleted file mode 100644 index 65d7a7a..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/bootstrap.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; - -function require(module) { - if (module.startsWith('./') && require.scopes.hasOwnProperty(module.slice(2))) { - return require.scopes[module.slice(2)]; - } - throw new Error('module: ' + module + ' not found.'); -} -require.scopes = {}; diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/incognito.js b/data/extensions/https-everywhere@eff.org/background-scripts/incognito.js deleted file mode 100644 index 7d4bc81..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/incognito.js +++ /dev/null @@ -1,73 +0,0 @@ -"use strict"; - -(function(exports) { - -// This file keeps track of incognito sessions, and clears any caches after -// an entire incognito session is closed (i.e. all incognito windows are closed). - -let state = { - incognito_session_exists: false, -}; - -function Incognito(onIncognitoDestruction) { - Object.assign(this, {onIncognitoDestruction}); - // Listen to window creation, so we can detect if an incognito window is created - if (chrome.windows) { - chrome.windows.onCreated.addListener(this.detect_incognito_creation); - } - - // Listen to window destruction, so we can clear caches if all incognito windows are destroyed - if (chrome.windows) { - chrome.windows.onRemoved.addListener(this.detect_incognito_destruction); - } -} - -Incognito.prototype = { - /** - * Detect if an incognito session is created, so we can clear caches when it's destroyed. - * - * @param window: A standard Window object. - */ - detect_incognito_creation: function(window_) { - if (window_.incognito === true) { - state.incognito_session_exists = true; - } - }, - - // If a window is destroyed, and an incognito session existed, see if it still does. - detect_incognito_destruction: async function() { - if (state.incognito_session_exists) { - if (!(await any_incognito_windows())) { - state.incognito_session_exists = false; - this.onIncognitoDestruction(); - } - } - }, -}; - -/** - * Check if any incognito window still exists - */ -function any_incognito_windows() { - return new Promise(resolve => { - chrome.windows.getAll(arrayOfWindows => { - for (let window_ of arrayOfWindows) { - if (window_.incognito === true) { - return resolve(true); - } - } - resolve(false); - }); - }); -} - -function onIncognitoDestruction(callback) { - return new Incognito(callback); -}; - -Object.assign(exports, { - onIncognitoDestruction, - state, -}); - -})(typeof exports == 'undefined' ? require.scopes.incognito = {} : exports); diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/ip_utils.js b/data/extensions/https-everywhere@eff.org/background-scripts/ip_utils.js deleted file mode 100644 index 90e0b1a..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/ip_utils.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict'; - -(function (exports) { - -/** - * Parse and convert literal IP address into numerical IP address. - * @param {string} ip - * @returns {number} - */ -const parseIp = ip => { - if (!/^[0-9.]+$/.test(ip)) { - return -1; - } - - /** @type {string[]} */ - const octets = ip.split('.'); - - if (octets.length !== 4) { - return -1; - } - - let ipN = 0; - - for (const octet of octets) { - if (octet === '') { - return -1; - } - - const octetN = parseInt(octet); - - if (octetN < 0 || octetN > 255) { - return -1; - } - - ipN = (ipN << 8) | octetN; - } - - return ipN >>> 0; -}; - -/** - * Check if the numeric IP address is within a certain range. - * @param {number} ip - * @param {number[]} range - * @returns {boolean} - */ -const isIpInRange = (ip, [rangeIp, mask]) => (ip & mask) >>> 0 === rangeIp; - -// A list of local IP address ranges -const localRanges = [ - [/* 0.0.0.0 */ 0x00000000, /* 255.255.255.255 */ 0xffffffff], - [/* 127.0.0.0 */ 0x7f000000, /* 255.0.0.0 */ 0xff000000], - [/* 10.0.0.0 */ 0x0a000000, /* 255.0.0.0 */ 0xff000000], - [/* 172.16.0.0 */ 0xac100000, /* 255.240.0.0 */ 0xfff00000], - [/* 192.168.0.0 */ 0xc0a80000, /* 255.255.0.0 */ 0xffff0000], -]; - -/** - * Check if the numeric IP address is inside the local IP address ranges. - * @param {number} ip - * @returns {boolean} - */ -const isLocalIp = ip => localRanges.some(range => isIpInRange(ip, range)); - -Object.assign(exports, { - parseIp, - isIpInRange, - isLocalIp -}); - -})(typeof exports !== 'undefined' ? exports : require.scopes.ip_utils = {}); diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/modules/ssl_codes.js b/data/extensions/https-everywhere@eff.org/background-scripts/modules/ssl_codes.js deleted file mode 100644 index ed955ec..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/modules/ssl_codes.js +++ /dev/null @@ -1,48 +0,0 @@ -"use strict"; - -/** - * @exports error_list - * @type {array} - * @description A list of known SSL config errors to filter through and not try to upgrade the user - * @see - * Chrome SSL errors: https://github.com/chromium/chromium/blob/master/components/domain_reliability/util.cc - * Firefox SSL Errors: https://hg.mozilla.org/releases/mozilla-release/file/tip/security/manager/locales/en-US/chrome/pipnss/nsserrors.properties - */ - -(function (exports) { - -const error_list = [ - "net::ERR_SSL_PROTOCOL_ERROR", - "net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH", - "net::ERR_SSL_UNRECOGNIZED_NAME_ALERT", - "net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN", - "net::ERR_CERT_COMMON_NAME_INVALID", - "net::ERR_CERT_DATE_INVALID", - "net::ERR_CERT_AUTHORITY_INVALID", - "net::ERR_CERT_REVOKED", - "net::ERR_CERT_INVALID", - "net::ERR_CONNECTION_CLOSED", - "net::ERR_CONNECTION_RESET", - "net::ERR_CONNECTION_REFUSED", - "net::ERR_CONNECTION_ABORTED", - "net::ERR_CONNECTION_FAILED", - "net::ERR_ABORTED", , - "NS_ERROR_CONNECTION_REFUSED", - "NS_ERROR_NET_ON_TLS_HANDSHAKE_ENDED", - "NS_BINDING_ABORTED", - "SSL received a record that exceeded the maximum permissible length.", - "Peer’s Certificate has expired.", - "Unable to communicate securely with peer: requested domain name does not match the server’s certificate.", - "Peer’s Certificate issuer is not recognized.", - "Peer’s Certificate has been revoked.", - "Peer reports it experienced an internal error.", - "The server uses key pinning (HPKP) but no trusted certificate chain could be constructed that matches the pinset. Key pinning violations cannot be overridden.", - "SSL received a weak ephemeral Diffie-Hellman key in Server Key Exchange handshake message.", - "The certificate was signed using a signature algorithm that is disabled because it is not secure.", - "Cannot communicate securely with peer: no common encryption algorithm(s).", - "SSL peer has no certificate for the requested DNS name." -]; - -Object.assign(exports, { error_list }); - -})(typeof exports !== 'undefined' ? exports : require.scopes.ssl_codes = {});
\ No newline at end of file diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/rules.js b/data/extensions/https-everywhere@eff.org/background-scripts/rules.js deleted file mode 100644 index 7f0a5b5..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/rules.js +++ /dev/null @@ -1,802 +0,0 @@ -"use strict"; - -(function(exports) { - -const util = require('./util'), - wasm = require('./wasm'); - -let settings = { - enableMixedRulesets: false, - domainBlacklist: new Set(), -}; - -// To reduce memory usage for the numerous rules/cookies with trivial rules -const trivial_cookie_rule_c = /.+/; - -/* A map of all scope RegExp objects */ -const scopes = new Map(); - -/** - * Returns the scope object from the map for the given scope string. - * @param {string} scope ruleset scope string - * @returns {RegExp} - */ -function getScope(scope) { - if (!scopes.has(scope)) { - scopes.set(scope, new RegExp(scope)); - } - return scopes.get(scope); -} - -/** - * Constructs a single rule - * @param from - * @param to - * @constructor - */ -function Rule(from, to) { - this.from_c = new RegExp(from); - this.to = to; -} - -// To reduce memory usage for the numerous rules/cookies with trivial rules -const trivial_rule = new Rule("^http:", "https:"); - -/** - * Returns a common trivial rule or constructs a new one. - */ -function getRule(from, to) { - if (from === "^http:" && to === "https:") { - // This is a trivial rule, rewriting http->https with no complex RegExp. - return trivial_rule; - } else { - // This is a non-trivial rule. - return new Rule(from, to); - } -} - -/** - * Generates a CookieRule - * @param host The host regex to compile - * @param cookiename The cookie name Regex to compile - * @constructor - */ -function CookieRule(host, cookiename) { - if (host === '.+') { - // Some cookie rules trivially match any host. - this.host_c = trivial_cookie_rule_c; - } else { - this.host_c = new RegExp(host); - } - - if (cookiename === '.+') { - // About 50% of cookie rules trivially match any name. - this.name_c = trivial_cookie_rule_c; - } else { - this.name_c = new RegExp(cookiename); - } -} - -/** - *A collection of rules - * @param {string} set_name The name of this set - * @param {boolean} default_state activity state - * @param {string} scope ruleset scope string - * @param {string} note Note will be displayed in popup - * @constructor - */ -function RuleSet(set_name, default_state, scope, note) { - this.name = set_name; - this.rules = []; - this.exclusions = null; - this.cookierules = null; - this.active = default_state; - this.default_state = default_state; - this.scope = getScope(scope); - this.note = note; -} - -RuleSet.prototype = { - /** - * Check if a URI can be rewritten and rewrite it - * @param urispec The uri to rewrite - * @returns {*} null or the rewritten uri - */ - apply: function(urispec) { - var returl = null; - // If we're covered by an exclusion, go home - if (this.exclusions !== null && this.exclusions.test(urispec)) { - util.log(util.DBUG, "excluded uri " + urispec); - return null; - } - - // Okay, now find the first rule that triggers - for (let rule of this.rules) { - returl = urispec.replace(rule.from_c, - rule.to); - if (returl != urispec) { - return returl; - } - } - return null; - }, - - /** - * Deep equivalence comparison - * @param ruleset The ruleset to compare with - * @returns true or false, depending on whether it's deeply equivalent - */ - isEquivalentTo: function(ruleset) { - if(this.name != ruleset.name || - this.note != ruleset.note || - this.active != ruleset.active || - this.default_state != ruleset.default_state) { - return false; - } - - try { - var this_exclusions_source = this.exclusions.source; - } catch(e) { - var this_exclusions_source = null; - } - - try { - var ruleset_exclusions_source = ruleset.exclusions.source; - } catch(e) { - var ruleset_exclusions_source = null; - } - - try { - var this_rules_length = this.rules.length; - } catch(e) { - var this_rules_length = 0; - } - - try { - var ruleset_rules_length = ruleset.rules.length; - } catch(e) { - var ruleset_rules_length = 0; - } - - if(this_rules_length != ruleset_rules_length) { - return false; - } - - if (this_exclusions_source != ruleset_exclusions_source) { - return false; - } - - if(this_rules_length > 0) { - for(let x = 0; x < this.rules.length; x++) { - if(this.rules[x].to != ruleset.rules[x].to) { - return false; - } - } - } - - return true; - } - -}; - -/** - * Initialize Rule Sets - * @constructor - */ -function RuleSets() { - // Load rules into structure - this.targets = new Map(); - - // A cache for potentiallyApplicableRulesets - this.ruleCache = new Map(); - - // A cache for cookie hostnames. - this.cookieHostCache = new Map(); - - /** - * A hash of rule name -> active status (true/false). - * @type {Object<string, boolean>} - */ - this.ruleActiveStates = {}; - - // The key to retrieve user rules from the storage api - this.USER_RULE_KEY = 'userRules'; - - return this; -} - - -RuleSets.prototype = { - - /** - * Load packaged rulesets, and rulesets in browser storage - * @param store object from store.js - */ - loadFromBrowserStorage: async function(store, applyStoredFunc) { - this.store = store; - this.ruleActiveStates = await this.store.get_promise('ruleActiveStates', {}); - try { - this.wasm_rs = wasm.RuleSets.new(); - } catch(e) { - util.log(util.WARN, 'Falling back to pure JS implementation: ' + e); - } - await applyStoredFunc(this); - await this.loadStoredUserRules(); - await this.addStoredCustomRulesets(); - }, - - /** - * Convert XML to JS and load rulesets - * @param {Document} ruleXml - * @param {string} scope - */ - addFromXml: function(ruleXml, scope) { - const rulesets_xml = ruleXml.getElementsByTagName("ruleset"); - - let rulesets = []; - for (let ruleset_xml of rulesets_xml) { - rulesets.push(this.convertOneXmlToJs(ruleset_xml)); - } - - this.addFromJson(rulesets, scope); - }, - - /** - * @param {*} ruleJson - * @param {string} scope - */ - addFromJson: function(ruleJson, scope) { - if (this.wasm_rs) { - this.wasm_rs.add_all_from_js_array( - ruleJson, - settings.enableMixedRulesets, - this.ruleActiveStates, - scope); - } else { - for (let ruleset of ruleJson) { - try { - this.parseOneJsonRuleset(ruleset, scope); - } catch(e) { - util.log(util.WARN, 'Error processing ruleset:' + e); - } - } - } - }, - - /** - * Parse one JSON format ruleset element - * @param {*} ruletag - * @param {string} scope - */ - parseOneJsonRuleset: function(ruletag, scope) { - var default_state = true; - var note = ""; - var default_off = ruletag["default_off"]; - if (default_off) { - default_state = false; - if (default_off === "user rule") { - default_state = true; - } - note += default_off + "\n"; - } - - // If a ruleset declares a platform, and we don't match it, treat it as - // off-by-default. In practice, this excludes "mixedcontent" rules. - var platform = ruletag["platform"]; - if (platform) { - default_state = false; - if (platform == "mixedcontent" && settings.enableMixedRulesets) { - default_state = true; - } - note += "Platform(s): " + platform + "\n"; - } - - var rule_set = new RuleSet(ruletag["name"], default_state, scope, note.trim()); - - // Read user prefs - if (rule_set.name in this.ruleActiveStates) { - rule_set.active = this.ruleActiveStates[rule_set.name]; - } - - var rules = ruletag["rule"]; - for (let rule of rules) { - if (rule["from"] != null && rule["to"] != null) { - rule_set.rules.push(getRule(rule["from"], rule["to"])); - } - } - - var exclusions = ruletag["exclusion"]; - if (exclusions != null) { - rule_set.exclusions = new RegExp(exclusions.join("|")); - } - - var cookierules = ruletag["securecookie"]; - if (cookierules != null) { - for (let cookierule of cookierules) { - if (cookierule["host"] != null && cookierule["name"] != null) { - if (!rule_set.cookierules) { - rule_set.cookierules = []; - } - rule_set.cookierules.push(new CookieRule(cookierule["host"], cookierule["name"])); - } - } - } - - var targets = ruletag["target"]; - for (let target of targets) { - if (target != null) { - if (!this.targets.has(target)) { - this.targets.set(target, []); - } - this.targets.get(target).push(rule_set); - } - } - }, - - /** - * Load a user rule - * @param params - * @param {string} scope - * @returns {boolean} - */ - addUserRule : function(params, scope) { - util.log(util.INFO, 'adding new user rule for ' + JSON.stringify(params)); - if (this.wasm_rs) { - this.wasm_rs.add_all_from_js_array( - [params], - settings.enableMixedRulesets, - this.ruleActiveStates, - scope); - } else { - this.parseOneJsonRuleset(params, scope); - } - - // clear cache so new rule take effect immediately - for (const target of params.target) { - this.ruleCache.delete(target); - } - - // TODO: maybe promote this rule? - util.log(util.INFO, 'done adding rule'); - return true; - }, - - /** - * Remove a user rule - * @param params - * @returns {boolean} - */ - removeUserRule: function(ruleset, src) { - /** - * FIXME: We have to use ruleset.name here because the ruleset itself - * carries no information on the target it is applying on. This also - * made it impossible for users to set custom ruleset name. - */ - util.log(util.INFO, 'removing user rule for ' + JSON.stringify(ruleset)); - - // Remove any cache from runtime - this.ruleCache.delete(ruleset.name); - - if (src === 'popup') { - - if (this.wasm_rs) { - this.wasm_rs.remove_ruleset(ruleset); - } else { - const tmp = this.targets.get(ruleset.name).filter(r => !r.isEquivalentTo(ruleset)); - this.targets.set(ruleset.name, tmp); - - if (this.targets.get(ruleset.name).length == 0) { - this.targets.delete(ruleset.name); - } - } - } - - if (src === 'options') { - /** - * FIXME: There is nothing we can do if the call comes from the - * option page because isEquivalentTo cannot work reliably. - * Leave the heavy duties to background.js to call initializeAllRules - */ - } - util.log(util.INFO, 'done removing rule'); - return true; - }, - - /** - * Retrieve stored user rules from storage api - **/ - getStoredUserRules: async function() { - return await this.store.get_promise(this.USER_RULE_KEY, []); - }, - - /** - * Load all stored user rules into this RuleSet object - */ - loadStoredUserRules: function() { - return this.getStoredUserRules() - .then(userRules => { - this.addFromJson(userRules, ''); - util.log(util.INFO, `loaded ${userRules.length} stored user rules`); - }); - }, - - /** - * Adds a new user rule - * @param params: params defining the rule - * @param cb: Callback to call after success/fail - * */ - addNewRuleAndStore: async function(params) { - if (this.addUserRule(params, '')) { - // If we successfully added the user rule, save it in the storage - // api so it's automatically applied when the extension is - // reloaded. - let userRules = await this.getStoredUserRules(); - // TODO: there's a race condition here, if this code is ever executed from multiple - // client windows in different event loops. - userRules.push(params); - // TODO: can we exceed the max size for storage? - await this.store.set_promise(this.USER_RULE_KEY, userRules); - } - }, - - /** - * Removes a user rule - * @param ruleset: the ruleset to remove - * */ - removeRuleAndStore: async function(ruleset, src) { - if (this.removeUserRule(ruleset, src)) { - let userRules = await this.getStoredUserRules(); - - if (src === 'popup') { - userRules = userRules.filter(r => - !(r.name === ruleset.name && r.rule[0].to === ruleset.rules[0].to) - ); - } - - if (src === 'options') { - userRules = userRules.filter(r => - !(r.name === ruleset.name && r.rule[0].to === ruleset.rule[0].to) - ); - } - await this.store.set_promise(this.USER_RULE_KEY, userRules); - } - }, - - addStoredCustomRulesets: function() { - return new Promise(resolve => { - this.store.get({ - legacy_custom_rulesets: [], - debugging_rulesets: "" - }, item => { - this.loadCustomRulesets(item.legacy_custom_rulesets); - this.loadCustomRuleset("<root>" + item.debugging_rulesets + "</root>"); - resolve(); - }); - }); - }, - - // Load in the legacy custom rulesets, if any - loadCustomRulesets: function(legacy_custom_rulesets) { - for(let legacy_custom_ruleset of legacy_custom_rulesets) { - this.loadCustomRuleset(legacy_custom_ruleset); - } - }, - - loadCustomRuleset: function(ruleset_string) { - this.addFromXml((new DOMParser()).parseFromString(ruleset_string, 'text/xml'), ''); - }, - - setRuleActiveState: async function(ruleset_name, active) { - if (active == undefined) { - delete this.ruleActiveStates[ruleset_name]; - } else { - this.ruleActiveStates[ruleset_name] = active; - } - await this.store.set_promise('ruleActiveStates', this.ruleActiveStates); - }, - - /** - * Converts an XML ruleset to a JS ruleset for parsing - * @param ruletag The whole <ruleset> tag to parse - */ - convertOneXmlToJs: function(ruletag) { - try { - let ruleset = {}; - - let default_off = ruletag.getAttribute("default_off"); - if (default_off) { - ruleset["default_off"] = default_off; - } - - let platform = ruletag.getAttribute("platform"); - if (platform) { - ruleset["platform"] = platform; - } - - let name = ruletag.getAttribute("name"); - if (name) { - ruleset["name"] = name; - } - - let rules = []; - for (let rule of ruletag.getElementsByTagName("rule")) { - rules.push({ - from: rule.getAttribute("from"), - to: rule.getAttribute("to") - }); - } - if (rules.length > 0) { - ruleset["rule"] = rules; - } - - let exclusions = []; - for (let exclusion of ruletag.getElementsByTagName("exclusion")) { - exclusions.push(exclusion.getAttribute("pattern")); - } - if (exclusions.length > 0) { - ruleset["exclusion"] = exclusions; - } - - let cookierules = []; - for (let cookierule of ruletag.getElementsByTagName("securecookie")) { - cookierules.push({ - host: cookierule.getAttribute("host"), - name: cookierule.getAttribute("name") - }); - } - if (cookierules.length > 0) { - ruleset["securecookie"] = cookierules; - } - - let targets = []; - for (let target of ruletag.getElementsByTagName("target")) { - targets.push(target.getAttribute("host")); - } - if (targets.length > 0) { - ruleset["target"] = targets; - } - - return ruleset; - } catch (e) { - util.log(util.WARN, 'Error converting ruleset to JS:' + e); - return {}; - } - }, - - /** - * Return a list of rulesets that apply to this host - * @param host The host to check - * @returns {*} (empty) list - */ - potentiallyApplicableRulesets: function(host) { - // Have we cached this result? If so, return it! - if (this.ruleCache.has(host)) { - let cached_item = this.ruleCache.get(host); - util.log(util.DBUG, "Ruleset cache hit for " + host + " items:" + cached_item.size); - return cached_item; - } else { - util.log(util.DBUG, "Ruleset cache miss for " + host); - } - - let results; - if (this.wasm_rs) { - let pa = this.wasm_rs.potentially_applicable(host); - results = new Set([...pa].map(ruleset => { - let rs = new RuleSet(ruleset.name, ruleset.default_state, ruleset.scope, ruleset.note); - - if (ruleset.cookierules) { - let cookierules = ruleset.cookierules.map(cookierule => { - return new CookieRule(cookierule.host, cookierule.name); - }); - rs.cookierules = cookierules; - } else { - rs.cookierules = null; - } - - let rules = ruleset.rules.map(rule => { - return getRule(rule.from, rule.to); - }); - rs.rules = rules; - - if (ruleset.exclusions) { - rs.exclusions = new RegExp(ruleset.exclusions); - } else { - rs.exclusions = null; - } - - rs.active = ruleset.active; - - return rs; - })); - } else { - // Let's begin search - results = (this.targets.has(host) ? - new Set([...this.targets.get(host)]) : - new Set()); - - let expressions = util.getWildcardExpressions(host); - for (const expression of expressions) { - results = (this.targets.has(expression) ? - new Set([...results, ...this.targets.get(expression)]) : - results); - } - - // Clean the results list, which may contain duplicates or undefined entries - results.delete(undefined); - - util.log(util.DBUG,"Applicable rules for " + host + ":"); - if (results.size == 0) { - util.log(util.DBUG, " None"); - results = util.nullIterable; - } else { - results.forEach(result => util.log(util.DBUG, " " + result.name)); - } - } - - // Insert results into the ruleset cache - this.ruleCache.set(host, results); - - // Cap the size of the cache. (Limit chosen somewhat arbitrarily) - if (this.ruleCache.size > 1000) { - // Map.prototype.keys() returns keys in insertion order, so this is a FIFO. - this.ruleCache.delete(this.ruleCache.keys().next().value); - } - - return results; - }, - - /** - * Check to see if the Cookie object c meets any of our cookierule criteria for being marked as secure. - * @param cookie The cookie to test - * @returns {*} true or false - */ - shouldSecureCookie: function(cookie) { - let hostname = cookie.domain; - // cookie domain scopes can start with . - while (hostname.charAt(0) == ".") { - hostname = hostname.slice(1); - } - - // Check if the domain might be being served over HTTP. If so, it isn't - // safe to secure a cookie! We can't always know this for sure because - // observing cookie-changed doesn't give us enough context to know the - // full origin URI. - - // First, if there are any redirect loops on this domain, don't secure - // cookies. XXX This is not a very satisfactory heuristic. Sometimes we - // would want to secure the cookie anyway, because the URLs that loop are - // not authenticated or not important. Also by the time the loop has been - // observed and the domain blacklisted, a cookie might already have been - // flagged as secure. - - if (settings.domainBlacklist.has(hostname)) { - util.log(util.INFO, "cookies for " + hostname + "blacklisted"); - return false; - } - - // Second, we need a cookie pass two tests before patching it - // (1) it is safe to secure the cookie, as per safeToSecureCookie() - // (2) it matches with the CookieRule - // - // We kept a cache of the results for (1), if we have a cached result which - // (a) is false, we should not secure the cookie for sure - // (b) is true, we need to perform test (2) - // - // Otherwise, - // (c) We need to perform (1) and (2) in place - - let safe = false; - if (this.cookieHostCache.has(hostname)) { - util.log(util.DBUG, "Cookie host cache hit for " + hostname); - safe = this.cookieHostCache.get(hostname); // true only if it is case (b) - if (!safe) { - return false; // (a) - } - } else { - util.log(util.DBUG, "Cookie host cache miss for " + hostname); - } - - const potentiallyApplicable = this.potentiallyApplicableRulesets(hostname); - for (const ruleset of potentiallyApplicable) { - if (ruleset.cookierules !== null && ruleset.active) { - // safe is false only indicate the lack of a cached result - // we cannot use it to avoid looping here - for (const cookierule of ruleset.cookierules) { - // if safe is true, it is case (b); otherwise it is case (c) - if (cookierule.host_c.test(cookie.domain) && cookierule.name_c.test(cookie.name)) { - return safe || this.safeToSecureCookie(hostname, potentiallyApplicable); - } - } - } - } - return false; - }, - - /** - * Check if it is secure to secure the cookie (=patch the secure flag in). - * @param domain The domain of the cookie - * @param potentiallyApplicable - * @returns {*} true or false - */ - safeToSecureCookie: function(domain, potentiallyApplicable) { - // Make up a random URL on the domain, and see if we would HTTPSify that. - var nonce_path = "/" + Math.random().toString(); - var test_uri = "http://" + domain + nonce_path + nonce_path; - - // Cap the size of the cookie cache (limit chosen somewhat arbitrarily) - if (this.cookieHostCache.size > 250) { - // Map.prototype.keys() returns keys in insertion order, so this is a FIFO. - this.cookieHostCache.delete(this.cookieHostCache.keys().next().value); - } - - util.log(util.INFO, "Testing securecookie applicability with " + test_uri); - for (let ruleset of potentiallyApplicable) { - if (ruleset.active && ruleset.apply(test_uri)) { - util.log(util.INFO, "Cookie domain could be secured."); - this.cookieHostCache.set(domain, true); - return true; - } - } - util.log(util.INFO, "Cookie domain could NOT be secured."); - this.cookieHostCache.set(domain, false); - return false; - }, - - /** - * Get a list of simple rules (active, with no exclusions) for all hosts that - * are in a single ruleset, and end in the specified ending. - * @param ending Target ending to search for - * @returns A list of { host, from_regex, to, scope_regex } - */ - getSimpleRulesEndingWith: function(ending) { - let results; - - if (this.wasm_rs) { - results = this.wasm_rs.get_simple_rules_ending_with(ending); - } else { - results = []; - for(let [host, rulesets] of this.targets) { - if (host.endsWith(ending) && - rulesets.length == 1 && - rulesets[0].active === true && - rulesets[0].exclusions == null - ) { - for (let rule of rulesets[0].rules) { - if (rule.from_c.test("http://" + host + "/")) { - results.push({ host, from_regex: rule.from_c.toString(), to: rule.to, scope_regex: rulesets[0].scope.toString() }); - } - } - } - } - } - return results; - }, - - /** - * Rewrite an URI - * @param urispec The uri to rewrite - * @param host The host of this uri - * @returns {*} the new uri or null - */ - rewriteURI: function(urispec, host) { - var newuri = null; - var potentiallyApplicable = this.potentiallyApplicableRulesets(host); - for (let ruleset of potentiallyApplicable) { - if (ruleset.active && (newuri = ruleset.apply(urispec))) { - return newuri; - } - } - return null; - } -}; - -Object.assign(exports, { - settings, - trivial_rule, - Rule, - RuleSet, - RuleSets, - getRule -}); - -})(typeof exports == 'undefined' ? require.scopes.rules = {} : exports); diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/store.js b/data/extensions/https-everywhere@eff.org/background-scripts/store.js deleted file mode 100644 index e3af7cb..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/store.js +++ /dev/null @@ -1,143 +0,0 @@ -"use strict"; - -(function(exports) { - -const rules = require('./rules'); -const util = require("./util"); - -function initialize() { - return new Promise(resolve => { - if (chrome.storage.sync) { - chrome.storage.sync.set({"sync-set-test": true}, () => { - if(chrome.runtime.lastError) { - setStorage(chrome.storage.local); - } else { - setStorage(chrome.storage.sync); - } - resolve(); - }); - } else { - setStorage(chrome.storage.local); - resolve(); - } - }); -} - -/* Storage promise setters and getters */ - -function generic_get_promise(key, default_val, storage) { - return new Promise(res => storage.get({[key]: default_val}, data => res(data[key]))); -} - -function generic_set_promise(key, value, storage) { - return new Promise(res => storage.set({[key]: value}, res)); -} - -function get_promise(key, default_val) { - return generic_get_promise(key, default_val, exports); -} - -function set_promise(key, value) { - return generic_set_promise(key, value, exports); -} - -function local_get_promise(key, default_val) { - return generic_get_promise(key, default_val, chrome.storage.local); -} - -function local_set_promise(key, value) { - return generic_set_promise(key, value, chrome.storage.local); -} - - - -async function performMigrations() { - let migration_version = await get_promise('migration_version', 0); - - try { - if (migration_version === 0) { - let ls = localStorage; - let ruleActiveStates = {}; - - for (let key in ls) { - if (ls.hasOwnProperty(key)) { - if (rules.RuleSets().USER_RULE_KEY === key) { - await set_promise(rules.RuleSets().USER_RULE_KEY, JSON.parse(ls[key])); - } else { - ruleActiveStates[key] = (ls[key] === "true"); - } - } - } - migration_version = 1; - await set_promise('migration_version', migration_version); - await set_promise('ruleActiveStates', ruleActiveStates); - } - - } catch (e) { - // do nothing - } - - if (migration_version <= 1) { - await get_promise(rules.RuleSets().USER_RULE_KEY, []) - .then(userRules => { - userRules = userRules.map(userRule => { - return { - name: userRule.host, - target: [userRule.host], - rule: [{ from: userRule.urlMatcher, to: userRule.redirectTo }], - default_off: "user rule" - }; - }); - return userRules; - }) - .then(userRules => { - return set_promise(rules.RuleSets().USER_RULE_KEY, userRules); - }); - - migration_version = 2; - await set_promise('migration_version', migration_version); - } - - if (migration_version <= 2) { - await get_promise('disabledList', []) - .then(disabledList => { - disabledList = disabledList.map(item => { - return util.getNormalisedHostname(item); - }); - return disabledList; - }) - .then(disabledList => { - return set_promise('disabledList', disabledList); - }); - - migration_version = 3; - await set_promise('migration_version', migration_version); - } -} - -const local = { - get: (...args) => chrome.storage.local.get(...args), - set: (...args) => chrome.storage.local.set(...args), - remove: (...args) => chrome.storage.local.remove(...args), - get_promise: local_get_promise, - set_promise: local_set_promise -}; - -function setStorage(store) { - Object.assign(exports, { - get: store.get.bind(store), - set: store.set.bind(store), - remove: store.remove.bind(store), - get_promise, - set_promise, - local - }); - chrome.runtime.sendMessage("store_initialized"); -} - -Object.assign(exports, { - initialize, - performMigrations -}); - -})(typeof exports == 'undefined' ? require.scopes.store = {} : exports); diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/update.js b/data/extensions/https-everywhere@eff.org/background-scripts/update.js deleted file mode 100644 index 4767f38..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/update.js +++ /dev/null @@ -1,480 +0,0 @@ -/* global pako */ - -"use strict"; - -let combined_update_channels, extension_version; -const { update_channels } = require('./update_channels'); -const wasm = require('./wasm'); - -// Determine if we're in the tests. If so, define some necessary components. -if (typeof window === "undefined") { - var WebCrypto = require("node-webcrypto-ossl"), - crypto = new WebCrypto(), - atob = require("atob"), - btoa = require("btoa"), - pako = require('../external/pako-1.0.5/pako_inflate.min.js'), - { TextDecoder } = require('text-encoding'), - chrome = require("sinon-chrome"), - window = { atob, btoa, chrome, crypto, pako, TextDecoder }, - fs = require('fs'); - - extension_version = JSON.parse(fs.readFileSync('./manifest.json')).version; - - combined_update_channels = update_channels; -} else { - extension_version = chrome.runtime.getManifest().version; -} - -(function(exports) { - -const util = require('./util'); - -let store, - background_callback; - -// how often we should check for new rulesets -const periodicity = 86400; - -const extension_date = new Date(extension_version.split('.').slice(0,3).join('-')); -const extension_timestamp = extension_date.getTime() / 1000; - -let imported_keys; - -// update channels are loaded from `background-scripts/update_channels.js` as well as the storage api -async function loadUpdateChannelsKeys() { - util.log(util.NOTE, 'Loading update channels and importing associated public keys.'); - - const stored_update_channels = await store.get_promise('update_channels', []); - const combined_update_channels_preflight = update_channels.concat(stored_update_channels); - - imported_keys = {}; - combined_update_channels = []; - - for(let update_channel of combined_update_channels_preflight) { - - try{ - imported_keys[update_channel.name] = await window.crypto.subtle.importKey( - "jwk", - update_channel.jwk, - { - name: "RSA-PSS", - hash: {name: "SHA-256"}, - }, - false, - ["verify"] - ); - combined_update_channels.push(update_channel); - util.log(util.NOTE, update_channel.name + ': Update channel key loaded.'); - } catch(err) { - util.log(util.WARN, update_channel.name + ': Could not import key. Aborting.'); - } - } -} - - -// Determine the time until we should check for new rulesets -async function timeToNextCheck() { - const last_checked = await store.local.get_promise('last-checked', false); - if(last_checked) { - const current_timestamp = Date.now() / 1000; - const secs_since_last_checked = current_timestamp - last_checked; - return Math.max(0, periodicity - secs_since_last_checked); - } else { - return 0; - } -} - -// Check for new rulesets immediately -async function resetTimer() { - await store.local.set_promise('last-checked', false); - destroyTimer(); - await createTimer(); -} - -// Check for new updates. If found, return the timestamp. If not, return false -async function checkForNewUpdates(update_channel) { - let timestamp_result = await fetch(update_channel.update_path_prefix + (update_channel.format == "bloom" ? "/latest-bloom-timestamp" : "/latest-rulesets-timestamp")); - if(timestamp_result.status == 200) { - let uc_timestamp = Number(await timestamp_result.text()); - - if((await store.local.get_promise('uc-timestamp: ' + update_channel.name, 0)) < uc_timestamp) { - return uc_timestamp; - } - } - return false; -} - -// Retrieve the timestamp for when an update channel was published -async function getUpdateChannelTimestamps() { - let timestamp_promises = []; - for(let update_channel of combined_update_channels) { - timestamp_promises.push(new Promise(async resolve => { - let timestamp = await store.local.get_promise('uc-stored-timestamp: ' + update_channel.name, 0); - resolve([update_channel, timestamp]); - })); - } - let timestamps = await Promise.all(timestamp_promises); - return timestamps; -} - -// Download and return new rulesets -async function getNewRulesets(rulesets_timestamp, update_channel) { - - store.local.set_promise('uc-timestamp: ' + update_channel.name, rulesets_timestamp); - - let signature_promise = fetch(update_channel.update_path_prefix + "/rulesets-signature." + rulesets_timestamp + ".sha256"); - let rulesets_promise = fetch(update_channel.update_path_prefix + "/default.rulesets." + rulesets_timestamp + ".gz"); - - let responses = await Promise.all([ - signature_promise, - rulesets_promise - ]); - - let resolutions = await Promise.all([ - responses[0].arrayBuffer(), - responses[1].arrayBuffer() - ]); - - return { - signature_array_buffer: resolutions[0], - rulesets_array_buffer: resolutions[1] - }; -} - -// Download and return new bloom -async function getNewBloom(bloom_timestamp, update_channel) { - store.local.set_promise('uc-timestamp: ' + update_channel.name, bloom_timestamp); - - let signature_promise = fetch(update_channel.update_path_prefix + "/bloom-signature." + bloom_timestamp + ".sha256"); - let bloom_metadata_promise = fetch(update_channel.update_path_prefix + "/bloom-metadata." + bloom_timestamp + ".json"); - let bloom_promise = fetch(update_channel.update_path_prefix + "/bloom." + bloom_timestamp + ".bin"); - - let responses = await Promise.all([ - signature_promise, - bloom_metadata_promise, - bloom_promise - ]); - - let resolutions = await Promise.all([ - responses[0].arrayBuffer(), - responses[1].arrayBuffer(), - responses[2].arrayBuffer() - ]); - - return { - signature_array_buffer: resolutions[0], - bloom_metadata_array_buffer: resolutions[1], - bloom_array_buffer: resolutions[2], - }; - -} - -// Returns a promise which verifies that the rulesets have a valid EFF -// signature, and if so, stores them and returns true. -// Otherwise, it throws an exception. -function verifyAndStoreNewRulesets(new_rulesets, rulesets_timestamp, update_channel) { - return new Promise((resolve, reject) => { - window.crypto.subtle.verify( - { - name: "RSA-PSS", - saltLength: 32 - }, - imported_keys[update_channel.name], - new_rulesets.signature_array_buffer, - new_rulesets.rulesets_array_buffer - ).then(async isvalid => { - if(isvalid) { - util.log(util.NOTE, update_channel.name + ': Downloaded ruleset signature checks out. Storing rulesets.'); - - const rulesets_gz = util.ArrayBufferToString(new_rulesets.rulesets_array_buffer); - const rulesets_byte_array = pako.inflate(rulesets_gz); - const rulesets = new TextDecoder("utf-8").decode(rulesets_byte_array); - const rulesets_json = JSON.parse(rulesets); - - if(rulesets_json.timestamp != rulesets_timestamp) { - reject(update_channel.name + ': Downloaded ruleset had an incorrect timestamp. This may be an attempted downgrade attack. Aborting.'); - } else { - await store.local.set_promise('rulesets: ' + update_channel.name, window.btoa(rulesets_gz)); - resolve(true); - } - } else { - reject(update_channel.name + ': Downloaded ruleset signature is invalid. Aborting.'); - } - }).catch(() => { - reject(update_channel.name + ': Downloaded ruleset signature could not be verified. Aborting.'); - }); - }); -} - -// Returns a promise which verifies that the bloom has a valid EFF -// signature, and if so, stores it and returns true. -// Otherwise, it throws an exception. -function verifyAndStoreNewBloom(new_bloom, bloom_timestamp, update_channel) { - return new Promise((resolve, reject) => { - window.crypto.subtle.verify( - { - name: "RSA-PSS", - saltLength: 32 - }, - imported_keys[update_channel.name], - new_bloom.signature_array_buffer, - new_bloom.bloom_metadata_array_buffer - ).then(async isvalid => { - if(isvalid) { - util.log(util.NOTE, update_channel.name + ': Bloom filter metadata signature checks out.'); - - const bloom_metadata = JSON.parse(util.ArrayBufferToString(new_bloom.bloom_metadata_array_buffer)); - const bloom_str = util.ArrayBufferToString(new_bloom.bloom_array_buffer); - - if(bloom_metadata.timestamp != bloom_timestamp) { - reject(update_channel.name + ': Downloaded bloom filter had an incorrect timestamp. This may be an attempted downgrade attack. Aborting.'); - } else if(await sha256sum(new_bloom.bloom_array_buffer) != bloom_metadata.sha256sum) { - reject(update_channel.name + ': sha256sum of the bloom filter is invalid. Aborting.'); - } else { - await store.local.set_promise('bloom: ' + update_channel.name, window.btoa(bloom_str)); - await store.local.set_promise('bloom_bitmap_bits: ' + update_channel.name, bloom_metadata.bitmap_bits); - await store.local.set_promise('bloom_k_num: ' + update_channel.name, bloom_metadata.k_num); - await store.local.set_promise('bloom_sip_keys_0_0: ' + update_channel.name, bloom_metadata.sip_keys[0][0]); - await store.local.set_promise('bloom_sip_keys_0_1: ' + update_channel.name, bloom_metadata.sip_keys[0][1]); - await store.local.set_promise('bloom_sip_keys_1_0: ' + update_channel.name, bloom_metadata.sip_keys[1][0]); - await store.local.set_promise('bloom_sip_keys_1_1: ' + update_channel.name, bloom_metadata.sip_keys[1][1]); - resolve(true); - } - } else { - reject(update_channel.name + ': Downloaded bloom filter metadata signature is invalid. Aborting.'); - } - }).catch(() => { - reject(update_channel.name + ': Downloaded bloom signature could not be verified. Aborting.'); - }); - }); -} - -async function sha256sum(buffer) { - const hashBuffer = await window.crypto.subtle.digest('SHA-256', buffer); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join(''); - return hashHex; -} - -function isNotUndefined(subject) { - return (typeof subject != 'undefined'); -} - -// Apply the rulesets we have stored. -async function applyStoredRulesets(rulesets_obj) { - let rulesets_promises = []; - for(let update_channel of combined_update_channels) { - if(update_channel.format == "rulesets" || !update_channel.format) { - rulesets_promises.push(new Promise(resolve => { - const key = 'rulesets: ' + update_channel.name; - chrome.storage.local.get(key, root => { - if(root[key]) { - util.log(util.NOTE, update_channel.name + ': Applying stored rulesets.'); - - const rulesets_gz = window.atob(root[key]); - const rulesets_byte_array = pako.inflate(rulesets_gz); - const rulesets_string = new TextDecoder("utf-8").decode(rulesets_byte_array); - const rulesets_json = JSON.parse(rulesets_string); - - resolve({json: rulesets_json, scope: update_channel.scope, replaces: update_channel.replaces_default_rulesets}); - } else { - resolve(); - } - }); - })); - } - } - - const rulesets_results = (await Promise.all(rulesets_promises)).filter(isNotUndefined); - - let replaces = false; - for(const rulesets_result of rulesets_results) { - if(rulesets_result.replaces === true) { - replaces = true; - } - rulesets_obj.addFromJson(rulesets_result.json.rulesets, rulesets_result.scope); - } - - if(!replaces) { - rulesets_obj.addFromJson(util.loadExtensionFile('rules/default.rulesets', 'json'), ''); - } -} - -// Apply the blooms we have stored. -async function applyStoredBlooms(bloom_arr) { - let bloom_promises = []; - for(let update_channel of combined_update_channels) { - if(update_channel.format == "bloom") { - bloom_promises.push(new Promise(resolve => { - const key = 'bloom: ' + update_channel.name; - chrome.storage.local.get(key, async root => { - if(root[key]) { - util.log(util.NOTE, update_channel.name + ': Applying stored bloom filter.'); - const bloom = util.StringToArrayBuffer(window.atob(root[key])); - const bloom_bitmap_bits = await store.local.get_promise('bloom_bitmap_bits: ' + update_channel.name, ""); - const bloom_k_num = await store.local.get_promise('bloom_k_num: ' + update_channel.name, ""); - const bloom_sip_keys_0_0 = await store.local.get_promise('bloom_sip_keys_0_0: ' + update_channel.name, ""); - const bloom_sip_keys_0_1 = await store.local.get_promise('bloom_sip_keys_0_1: ' + update_channel.name, ""); - const bloom_sip_keys_1_0 = await store.local.get_promise('bloom_sip_keys_1_0: ' + update_channel.name, ""); - const bloom_sip_keys_1_1 = await store.local.get_promise('bloom_sip_keys_1_1: ' + update_channel.name, ""); - - try{ - resolve(wasm.Bloom.from_existing(bloom, bloom_bitmap_bits, bloom_k_num, [[bloom_sip_keys_0_0, bloom_sip_keys_0_1], [bloom_sip_keys_1_0, bloom_sip_keys_1_1]])); - } catch(_) { - resolve(); - } - } else { - resolve(); - } - }); - })); - } - } - - bloom_arr.length = 0; - const bloom_results = (await Promise.all(bloom_promises)).filter(isNotUndefined); - for(const bloom_result of bloom_results) { - bloom_arr.push(bloom_result); - } -} - - -// basic workflow for periodic checks -async function performCheck() { - util.log(util.NOTE, 'Checking for new updates.'); - - const current_timestamp = Date.now() / 1000; - store.local.set_promise('last-checked', current_timestamp); - - let num_updates = 0; - for(let update_channel of combined_update_channels) { - if(update_channel.format == "bloom") { - let new_bloom_timestamp = await checkForNewUpdates(update_channel); - if(new_bloom_timestamp) { - util.log(util.NOTE, update_channel.name + ': A new bloom filter has been released. Downloading now.'); - let new_bloom = await getNewBloom(new_bloom_timestamp, update_channel); - try{ - await verifyAndStoreNewBloom(new_bloom, new_bloom_timestamp, update_channel); - store.local.set_promise('uc-stored-timestamp: ' + update_channel.name, new_bloom_timestamp); - num_updates++; - } catch(err) { - util.log(util.WARN, update_channel.name + ': ' + err); - } - } - } else { - let new_rulesets_timestamp = await checkForNewUpdates(update_channel); - if(new_rulesets_timestamp) { - - if(update_channel.replaces_default_rulesets && extension_timestamp > new_rulesets_timestamp) { - util.log(util.NOTE, update_channel.name + ': A new ruleset bundle has been released, but it is older than the extension-bundled rulesets it replaces. Skipping.'); - continue; - } - - util.log(util.NOTE, update_channel.name + ': A new ruleset bundle has been released. Downloading now.'); - let new_rulesets = await getNewRulesets(new_rulesets_timestamp, update_channel); - try{ - await verifyAndStoreNewRulesets(new_rulesets, new_rulesets_timestamp, update_channel); - store.local.set_promise('uc-stored-timestamp: ' + update_channel.name, new_rulesets_timestamp); - num_updates++; - } catch(err) { - util.log(util.WARN, update_channel.name + ': ' + err); - } - } - } - } - if(num_updates > 0) { - background_callback(); - } -}; - -async function storageListener(changes, areaName) { - if (areaName === 'sync' || areaName === 'local') { - if ('autoUpdateRulesets' in changes) { - if (changes.autoUpdateRulesets.newValue) { - await createTimer(); - } else { - destroyTimer(); - } - } - } - - if ('update_channels' in changes) { - await loadUpdateChannelsKeys(); - } -}; - -function addStorageListener() { - chrome.storage.onChanged.addListener(storageListener); -} - -function removeStorageListener() { - chrome.storage.onChanged.removeListener(storageListener); -} - -addStorageListener(); - -let initialCheck, - subsequentChecks; - -async function createTimer() { - const time_to_next_check = await timeToNextCheck(); - - initialCheck = setTimeout(() => { - performCheck(); - subsequentChecks = setInterval(performCheck, periodicity * 1000); - }, time_to_next_check * 1000); -} - -function destroyTimer() { - if (initialCheck) { - clearTimeout(initialCheck); - } - if (subsequentChecks) { - clearInterval(subsequentChecks); - } -} - -function clear_replacement_update_channels() { - let keys = []; - for (const update_channel of combined_update_channels) { - if(update_channel.replaces_default_rulesets) { - util.log(util.NOTE, update_channel.name + ': You have a new version of the extension. Clearing any stored rulesets, which replace the new extension-bundled ones.'); - keys.push('uc-timestamp: ' + update_channel.name); - keys.push('uc-stored-timestamp: ' + update_channel.name); - keys.push('rulesets: ' + update_channel.name); - } - } - - return new Promise(resolve => { - chrome.storage.local.remove(keys, resolve); - }); -} - -async function initialize(store_param, cb) { - store = store_param; - background_callback = cb; - - await loadUpdateChannelsKeys(); - - if (await store.local.get_promise('extensionTimestamp', 0) !== extension_timestamp) { - await clear_replacement_update_channels(); - await store.local.set_promise('extensionTimestamp', extension_timestamp); - } - - if (await store.get_promise('autoUpdateRulesets', false)) { - await createTimer(); - } -} - -Object.assign(exports, { - applyStoredRulesets, - applyStoredBlooms, - initialize, - getUpdateChannelTimestamps, - resetTimer, - loadUpdateChannelsKeys, - addStorageListener, - removeStorageListener, -}); - -})(typeof exports == 'undefined' ? require.scopes.update = {} : exports); diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/update_channels.js b/data/extensions/https-everywhere@eff.org/background-scripts/update_channels.js deleted file mode 100644 index 80494ef..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/update_channels.js +++ /dev/null @@ -1,44 +0,0 @@ -/* exported update_channels */ - -'use strict'; - -(function (exports) { - -exports.update_channels = [ - { - name: 'EFF (Full)', - jwk: { - kty: 'RSA', - e: 'AQAB', - n: '1cwvFQu3Kw-Pz8bcEFuV5zx0ZheDsc4Tva7Qv6BL90_sDLqCW79Y543nDkPtNVfFH_89pt2kSPp_IcS5XnYiw6zBQeFuILFw5JpvZt14K0s4' + - 'e025Q9CXfhYKIBKT9PnqihwAacjMa6rQb7RTu7XxVvqxRb3b0vx2CR40LSlYZ8H_KpeaUwq2oz-fyrI6LFTeYvbO3ZuLKeK5xV1a32xeTVMF' + - 'kIj3LxnQalxq-DRHfj7LRRoTnbRDW4uoDc8aVpLFliuO79jUKbobz4slpiWJ4wjKR_O6OK13HbZUiOSxi8Bms-UqBPOyzbMVpmA7lv_zWdaL' + - 'u1IVlVXQyLVbbrqI6llRqfHdcJoEl-eC48AofuB-relQtjTEK_hyBf7sPwrbqAarjRjlyEx6Qy5gTXyxM9attfNAeupYR6jm8LKm6TFpfWky' + - 'DxUmj_f5pJMBWNTomV74f8iQ2M18_KWMUDCOf80tR0t21Q1iCWdvA3K_KJn05tTLyumlwwlQijMqRkYuao-CX9L3DJIaB3VPYPTSIPUr7oi1' + - '6agsuamOyiOtlZiRpEvoNg2ksJMZtwnj5xhBQydkdhMW2ZpHDzcLuZlhJYZL_l3_7wuzRM7vpyA9obP92CpZRFJErGZmFxJC93I4U9-0B0wg' + - '-sbyMKGJ5j1BWTnibCklDXtWzXtuiz18EgE' - }, - update_path_prefix: 'https://www.https-rulesets.org/v1/', - scope: '', - replaces_default_rulesets: true - }, - { - name: 'DuckDuckGo Smarter Encryption', - format: 'bloom', - jwk: { - kty: 'RSA', - e: 'AQAB', - n: '1cwvFQu3Kw-Pz8bcEFuV5zx0ZheDsc4Tva7Qv6BL90_sDLqCW79Y543nDkPtNVfFH_89pt2kSPp_IcS5XnYiw6zBQeFuILFw5JpvZt14K0s4' + - 'e025Q9CXfhYKIBKT9PnqihwAacjMa6rQb7RTu7XxVvqxRb3b0vx2CR40LSlYZ8H_KpeaUwq2oz-fyrI6LFTeYvbO3ZuLKeK5xV1a32xeTVMF' + - 'kIj3LxnQalxq-DRHfj7LRRoTnbRDW4uoDc8aVpLFliuO79jUKbobz4slpiWJ4wjKR_O6OK13HbZUiOSxi8Bms-UqBPOyzbMVpmA7lv_zWdaL' + - 'u1IVlVXQyLVbbrqI6llRqfHdcJoEl-eC48AofuB-relQtjTEK_hyBf7sPwrbqAarjRjlyEx6Qy5gTXyxM9attfNAeupYR6jm8LKm6TFpfWky' + - 'DxUmj_f5pJMBWNTomV74f8iQ2M18_KWMUDCOf80tR0t21Q1iCWdvA3K_KJn05tTLyumlwwlQijMqRkYuao-CX9L3DJIaB3VPYPTSIPUr7oi1' + - '6agsuamOyiOtlZiRpEvoNg2ksJMZtwnj5xhBQydkdhMW2ZpHDzcLuZlhJYZL_l3_7wuzRM7vpyA9obP92CpZRFJErGZmFxJC93I4U9-0B0wg' + - '-sbyMKGJ5j1BWTnibCklDXtWzXtuiz18EgE' - }, - update_path_prefix: 'https://www.https-rulesets.org/ddg/', - scope: '', - } -]; - -})(typeof exports === 'undefined' ? require.scopes.update_channels = {} : exports); diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/util.js b/data/extensions/https-everywhere@eff.org/background-scripts/util.js deleted file mode 100644 index e2b069b..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/util.js +++ /dev/null @@ -1,183 +0,0 @@ -"use strict"; - -(function(exports) { - -var VERB = 1; -var DBUG = 2; -var INFO = 3; -var NOTE = 4; -var WARN = 5; -// FYI: Logging everything is /very/ slow. Chrome will log & buffer -// these console logs even when the debug tools are closed. :( - -// TODO: Add an easy UI to change the log level. -// (Developers can just type DEFAULT_LOG_LEVEL=VERB in the console) -var DEFAULT_LOG_LEVEL = NOTE; -console.log("Hey developer! Want to see more verbose logging?"); -console.log("Type this into the console: let util = require('./util'); util.setDefaultLogLevel(util.VERB);"); -console.log("Accepted levels are VERB, DBUG, INFO, NOTE and WARN, default is NOTE"); - -function log(level, str) { - if (level >= DEFAULT_LOG_LEVEL) { - if (level === WARN) { - // Show warning with a little yellow icon in Chrome. - console.warn(str); - } else { - console.log(str); - } - } -} - -function setDefaultLogLevel(level) { - DEFAULT_LOG_LEVEL = level; -} - -function getDefaultLogLevel() { - return DEFAULT_LOG_LEVEL; -} - -/** - * Load a file packaged with the extension - * - * @param url: a relative URL to local file - */ -function loadExtensionFile(url, returnType) { - var xhr = new XMLHttpRequest(); - // Use blocking XHR to ensure everything is loaded by the time - // we return. - xhr.open("GET", chrome.runtime.getURL(url), false); - xhr.send(null); - // Get file contents - if (xhr.readyState !== 4) { - return; - } - if (returnType === 'xml') { - return xhr.responseXML; - } - if (returnType === 'json') { - return JSON.parse(xhr.responseText); - } - return xhr.responseText; -} - -/** - * Remove tailing dots from hostname, e.g. "www.example.com." - * Preserve port numbers if they are used - */ -function getNormalisedHostname(host) { - let [ hostname, port ] = host.split(":"); - while (hostname && hostname[hostname.length - 1] === '.' && hostname !== '.') { - hostname = hostname.slice(0, -1); - } - if (port) { - return `${hostname}:${port}`; - } - return hostname; -} - -// Empty iterable singleton to reduce memory usage -const nullIterable = Object.create(null, { - [Symbol.iterator]: { - value: function* () { - // do nothing - } - }, - - size: { - value: 0 - }, -}); - -/** - * Return true if host is well-formed (RFC 1035) - */ -function isValidHostname(host) { - if (host && host.length > 0 && host.length <= 255 && host.indexOf("..") === -1) { - return true; - } - return false; -} - -/** - * Return a list of wildcard expressions which support - * the host under HTTPS Everywhere's implementation - */ -function getWildcardExpressions(host) { - // Ensure host is well-formed (RFC 1035) - if (!isValidHostname(host)) { - return nullIterable; - } - - // Ensure host does not contain a wildcard itself - if (host.indexOf("*") != -1) { - return nullIterable; - } - - let results = []; - - // Replace www.example.com with www.example.* - // eat away from the right for once and only once - let segmented = host.split("."); - if (segmented.length > 1) { - const tmp = [...segmented.slice(0, segmented.length - 1), "*"].join("."); - results.push(tmp); - } - - // now eat away from the left, with *, so that for x.y.z.google.com we - // check *.y.z.google.com, *.z.google.com and *.google.com - for (let i = 1; i < segmented.length - 1; i++) { - const tmp = ["*", ...segmented.slice(i, segmented.length)].join("."); - results.push(tmp); - } - return results; -} - -/** - * Convert an ArrayBuffer to string - * - * @param array: an ArrayBuffer to convert - */ -function ArrayBufferToString(ab) { - let array = new Uint8Array(ab); - let string = ""; - - for (let byte of array) { - string += String.fromCharCode(byte); - } - - return string; -} - -/** - * Convert a string to an ArrayBuffer - * - * @param string: a string to convert - */ -function StringToArrayBuffer(str) { - var byteArray = new Uint8Array(str.length); - for (var i = 0; i < str.length; i++) { - byteArray[i] = str.charCodeAt(i); - } - return byteArray; -} - - -Object.assign(exports, { - VERB, - DBUG, - INFO, - NOTE, - WARN, - log, - nullIterable, - isValidHostname, - getNormalisedHostname, - getWildcardExpressions, - setDefaultLogLevel, - getDefaultLogLevel, - loadExtensionFile, - ArrayBufferToString, - StringToArrayBuffer -}); - -})(typeof exports == 'undefined' ? require.scopes.util = {} : exports); diff --git a/data/extensions/https-everywhere@eff.org/background-scripts/wasm.js b/data/extensions/https-everywhere@eff.org/background-scripts/wasm.js deleted file mode 100644 index 551b1d3..0000000 --- a/data/extensions/https-everywhere@eff.org/background-scripts/wasm.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; - -(function(exports) { - -const util = require('./util'), - { RuleSets, Bloom } = wasm_bindgen; - -async function initialize() { - try { - await wasm_bindgen(chrome.runtime.getURL('wasm/https_everywhere_lib_wasm_bg.wasm')); - } catch(e) { - util.log(util.WARN, 'The wasm library has not loaded correctly: ' + e); - } -} - -function is_enabled() { - return true; -} - -Object.assign(exports, { - initialize, - RuleSets, - Bloom, - is_enabled, -}); - -})(typeof exports == 'undefined' ? require.scopes.wasm = {} : exports); |