summaryrefslogtreecommitdiff
path: root/data/extensions/https-everywhere@eff.org/background-scripts
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/https-everywhere@eff.org/background-scripts')
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/background.js959
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/bootstrap.js9
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/incognito.js73
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/ip_utils.js71
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/modules/ssl_codes.js48
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/rules.js802
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/store.js143
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/update.js480
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/update_channels.js44
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/util.js183
-rw-r--r--data/extensions/https-everywhere@eff.org/background-scripts/wasm.js27
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);