543 lines
16 KiB
JavaScript
543 lines
16 KiB
JavaScript
/** \file
|
|
* \brief Code that updates configuration stored by the user after upgrades
|
|
*
|
|
* \author Copyright (C) 2019 Martin Timko
|
|
* \author Copyright (C) 2019-2021 Libor Polcak
|
|
*
|
|
* \license SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
let defaultNbsWhitelistDomain = "duckduckgo.com";
|
|
let defaultNbsWhitelist = {};
|
|
defaultNbsWhitelist[defaultNbsWhitelistDomain] = true;
|
|
|
|
async function installUpdate() {
|
|
/**
|
|
* 0.3+ storage
|
|
* {
|
|
* __default__: 2, // Default protection level
|
|
* version: 2.2, // The version of this storage
|
|
* custom_levels: {}, // associative array of custom level (key, its id => object)
|
|
* {level_id: short string used for example on the badge
|
|
* level_text: Short level description
|
|
* level_description: Full level description
|
|
* ...
|
|
* wrapping_params (key-value pairs), see wrapping_groups for the list of params and
|
|
* supported values
|
|
* }
|
|
* domains: {}, // associative array of levels associated with specific domains (key, the domain => object)
|
|
* {level_id: short string of the level in use
|
|
* }
|
|
* nbsWhitelist: {} // associative array of hosts that are removed from http protection control (hostname => boolean)
|
|
* requestShieldOn: {} // Boolean, if it's TRUE or undefined, the http request protection is turned on, if it's FALSE, the protection si turned off
|
|
* fpDetectionOn: {} // Boolean, if it's TRUE, the fingerprint detection is turned on, if it's FALSE or undefined, the protection si turned off
|
|
*
|
|
*
|
|
*/
|
|
let item = await browser.storage.sync.get(null);
|
|
if (!item.hasOwnProperty("version") || (item.version < 2.1)) {
|
|
browser.storage.sync.clear();
|
|
console.log("All JavaScript Restrictor data cleared! Unfortunately, we do not migrate settings from versions bellow 0.3.");
|
|
item = {
|
|
__default__: 2,
|
|
version: 2.1,
|
|
custom_levels: {},
|
|
domains: {},
|
|
};
|
|
}
|
|
if (item.version == 2.1) {
|
|
// No Geolocation below 2.2
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (l.time_precision) {
|
|
l.geolocation = true;
|
|
if (l.time_precision_randomize) {
|
|
l.geolocation_locationObfuscationType = 5;
|
|
}
|
|
else if (l.time_precision_precision == 2) {
|
|
l.geolocation_locationObfuscationType = 2;
|
|
}
|
|
else if (l.time_precision_precision == 1) {
|
|
l.geolocation_locationObfuscationType = 3;
|
|
}
|
|
else if (l.time_precision_precision == 0) {
|
|
l.geolocation_locationObfuscationType = 4;
|
|
}
|
|
else {
|
|
l.geolocation_locationObfuscationType = -1;
|
|
}
|
|
// note that the obfuscation type might be redefined below
|
|
}
|
|
if (l.shared_array || l.webworker || l.xhr || l.arrays) {
|
|
l.geolocation = true;
|
|
l.geolocation_locationObfuscationType = 0;
|
|
}
|
|
if (l.geolocation_locationObfuscationType === undefined && l.htmlcanvaselement) {
|
|
l.geolocation = true;
|
|
l.geolocation_locationObfuscationType = 3;
|
|
}
|
|
}
|
|
item.version = 2.2;
|
|
}
|
|
if (item.version == 2.2) {
|
|
// No window.name wrapping below 2.2
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
let count = 0;
|
|
count += Number(Boolean(l.time_precision)) +
|
|
Number(Boolean(l.hardware)) +
|
|
Number(Boolean(l.battery)) +
|
|
Number(Boolean(l.geolocation)) +
|
|
Number(Boolean(l.shared_array)) +
|
|
Number(Boolean(l.webworker)) +
|
|
Number(Boolean(l.xhr)) +
|
|
Number(Boolean(l.arrays)) +
|
|
Number(Boolean(l.htmlcanvaselement));
|
|
if (count >= 3) {
|
|
l.windowname = true;
|
|
}
|
|
}
|
|
item.version = 2.3;
|
|
}
|
|
if (item.version == 2.3) {
|
|
// No enumerateDevices wrapping below 2.4
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
let count = 0;
|
|
count += Number(Boolean(l.time_precision)) +
|
|
Number(Boolean(l.hardware)) +
|
|
Number(Boolean(l.battery)) +
|
|
Number(Boolean(l.geolocation)) +
|
|
Number(Boolean(l.shared_array)) +
|
|
Number(Boolean(l.webworker)) +
|
|
Number(Boolean(l.xhr)) +
|
|
Number(Boolean(l.arrays)) +
|
|
100*Number(Boolean(l.htmlcanvaselement));
|
|
if (count >= 102) {
|
|
l.enumerateDevices = true;
|
|
}
|
|
}
|
|
item.version = 2.4;
|
|
}
|
|
if (item.version < 2.6) {
|
|
// No Beacon API (analytics) wrapping below 2.6
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (l.windowname || l.battery || l.geolocation || l.enumerateDevices || l.time_precision || l.hardware) {
|
|
l.analytics = true;
|
|
}
|
|
}
|
|
item.version = 2.6;
|
|
}
|
|
if (item.version < 2.7) {
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (l.htmlcanvaselement) {
|
|
l.htmlcanvaselement_method = 1;
|
|
l.audiobuffer = true;
|
|
l.audiobuffer_method = 0;
|
|
l.webgl = true;
|
|
l.webgl_method = 0;
|
|
}
|
|
}
|
|
item.version = 2.7;
|
|
}
|
|
if (item.version < 2.8) {
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (l.htmlcanvaselement) {
|
|
l.plugins = true;
|
|
if (l.htmlcanvaselement_method == 0) {
|
|
l.plugins_method = 0;
|
|
}
|
|
else {
|
|
l.plugins_method = 2;
|
|
}
|
|
}
|
|
}
|
|
item.version = 2.8;
|
|
}
|
|
if (item.version < 2.9) {
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (l.analytics) {
|
|
l.gamepads = true;
|
|
}
|
|
}
|
|
item.version = 2.9;
|
|
}
|
|
if (item.version < 3) {
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (l.gamepads) {
|
|
l.vr = true;
|
|
}
|
|
}
|
|
item.version = 3;
|
|
}
|
|
if (item.version < 4) {
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (l.hardware || l.battery || l.windowname) {
|
|
l.physical_environment = true;
|
|
l.physical_environment_emulateStationaryDevice = true;
|
|
}
|
|
}
|
|
item.version = 4;
|
|
}
|
|
if (item.version < 5) {
|
|
item.fpDetectionOn = false;
|
|
item.version = 5;
|
|
}
|
|
if (item.version < 6) {
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
// time_precision
|
|
if (l.time_precision) {
|
|
if (l.time_precision_randomize || l.time_precision_precision < 1) {
|
|
l.time_precision = 3;
|
|
}
|
|
else if (l.time_precision_precision == 1) {
|
|
l.time_precision = 2;
|
|
}
|
|
else if (l.time_precision_precision > 1) {
|
|
l.time_precision = 1;
|
|
}
|
|
else {
|
|
delete l.time_precision;
|
|
}
|
|
}
|
|
else {
|
|
delete l.time_precision;
|
|
}
|
|
delete l.time_precision_precision;
|
|
delete l.time_precision_randomize;
|
|
// regular farbling
|
|
for (farblable of ["htmlcanvaselement", "audiobuffer", "webgl", "plugins", "enumerateDevices", "hardware"]) {
|
|
if (l[farblable]) {
|
|
if (l[farblable + "_method"] !== undefined) {
|
|
l[farblable] = l[farblable + "_method"] + 1;
|
|
}
|
|
else {
|
|
l[farblable] = 2;
|
|
}
|
|
}
|
|
else {
|
|
delete l[farblable];
|
|
}
|
|
delete l[farblable + "_method"];
|
|
}
|
|
// xhr
|
|
if (l.xhr) {
|
|
if (l.xhr_behaviour_block && !l.xhr_behaviour_ask) {
|
|
l.xhr = 2;
|
|
}
|
|
else {
|
|
l.xhr = 1;
|
|
}
|
|
}
|
|
else {
|
|
delete l.xhr;
|
|
}
|
|
delete l.xhr_behaviour_block;
|
|
delete l.xhr_behaviour_ask;
|
|
// arrays
|
|
if (l.arrays) {
|
|
if (l.arrays_mapping) {
|
|
l.arrays = 2;
|
|
}
|
|
else {
|
|
l.arrays = 1;
|
|
}
|
|
}
|
|
else {
|
|
delete l.arrays;
|
|
}
|
|
delete l.arrays_mapping;
|
|
//sharred_array
|
|
if (l.shared_array) {
|
|
if (l.shared_array_approach_block) {
|
|
l.shared_array = 2;
|
|
}
|
|
else {
|
|
l.shared_array = 1;
|
|
}
|
|
}
|
|
else {
|
|
delete l.shared_array;
|
|
}
|
|
delete l.shared_array_approach_block;
|
|
delete l.shared_array_approach_polyfill;
|
|
// webworker
|
|
if (l.webworker) {
|
|
if (l.webworker_approach_polyfill) {
|
|
l.webworker = 2;
|
|
}
|
|
else {
|
|
l.webworker = 1;
|
|
}
|
|
}
|
|
else {
|
|
delete l.webworker;
|
|
}
|
|
delete l.webworker_approach_polyfill;
|
|
delete l.webworker_approach_slow;
|
|
// geolocation
|
|
if (l.geolocation) {
|
|
if (l.geolocation_locationObfuscationType == 0) {
|
|
l.geolocation = 6;
|
|
}
|
|
else if (l.geolocation_locationObfuscationType == 2) {
|
|
l.geolocation = 2;
|
|
}
|
|
else if (l.geolocation_locationObfuscationType == 3) {
|
|
l.geolocation = 3;
|
|
}
|
|
else if (l.geolocation_locationObfuscationType == 4) {
|
|
l.geolocation = 4;
|
|
}
|
|
else if (l.geolocation_locationObfuscationType == 5) {
|
|
l.geolocation = 5;
|
|
}
|
|
else if (l.geolocation_locationObfuscationType == -1) {
|
|
l.geolocation = 1;
|
|
}
|
|
else {
|
|
l.geolocation = 3;
|
|
}
|
|
}
|
|
else {
|
|
delete l.geolocation;
|
|
}
|
|
delete l.geolocation_locationObfuscationType;
|
|
// others
|
|
for (wrap of ["physical_environment", "gamepads", "vr", "analytics", "battery", "windowname"]) {
|
|
if (l[wrap]) {
|
|
l[wrap] = 1;
|
|
}
|
|
else {
|
|
delete l[wrap];
|
|
}
|
|
}
|
|
delete l.physical_environment_emulateStationaryDevice;
|
|
}
|
|
item.version = 6;
|
|
}
|
|
if (item.version < 6.1) {
|
|
// We no longer ship level 1 as its content is not defined well, the tweaks allow the user
|
|
// to relax the level conditions specifically to each page
|
|
for (domain in item.domains) {
|
|
let level = item.domains[domain];
|
|
// Select the default level if the default level is not 3 (if level 3 is the default, set
|
|
// level 2)
|
|
if (level.level_id == "1") {
|
|
if ([2, 3, "2", "3"].includes(item.__default__)) {
|
|
level.level_id = "2";
|
|
}
|
|
else {
|
|
level.level_id = String(item.__default__);
|
|
}
|
|
}
|
|
}
|
|
item.version = 6.1;
|
|
}
|
|
if (item.version < 6.2) {
|
|
if (item.fpDetectionOn) {
|
|
item.fpdSettings = {
|
|
behavior: 3
|
|
};
|
|
}
|
|
else {
|
|
item.fpdSettings = {
|
|
behavior: 1
|
|
};
|
|
}
|
|
item.fpDetectionOn = true;
|
|
item.nbsWhitelist = item.whitelistedHosts ? item.whitelistedHosts : defaultNbsWhitelist;
|
|
delete item.whitelistedHosts;
|
|
item.nbsSettings = {
|
|
notifications: 1
|
|
};
|
|
item.version = 6.2;
|
|
}
|
|
if (item.version < 6.3) {
|
|
if (level_2.windowname === undefined) { // Firefox
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (!(l.arrays || l.shared_array)) {
|
|
delete l.windowname;
|
|
}
|
|
}
|
|
}
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
l.webworker = 2;
|
|
}
|
|
item.version = 6.3;
|
|
}
|
|
if (item.version < 6.4) {
|
|
if (item.nbsSettings) {
|
|
item.nbsSettings.blocking = 1;
|
|
}
|
|
if (item.fpdSettings) {
|
|
item.fpdSettings.detection = 0;
|
|
}
|
|
item.version = 6.4;
|
|
}
|
|
if (item.version < 6.5) {
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (l.time_precision && l.analytics && l.geolocation) {
|
|
l.coopschedule = 1;
|
|
l.net = 1;
|
|
l.nfc = 1;
|
|
l.useridle = 1;
|
|
if (l.htmlcanvaselement || l.audiobuffer || l.enumerateDevices) {
|
|
l.useridle = 2;
|
|
if (l.htmlcanvaselement === 2 || l.audiobuffer === 2 || l.enumerateDevices === 3) {
|
|
l.useridle = 3;
|
|
l.playback = 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
item.version = 6.5;
|
|
}
|
|
if (item.version < 6.6) {
|
|
if (item.fpdSettings) {
|
|
switch (item.fpdSettings.behavior) {
|
|
case 0:
|
|
item.fpdSettings.notifications = 0;
|
|
break;
|
|
case 1:
|
|
item.fpdSettings.behavior = 0;
|
|
item.fpdSettings.notifications = 1;
|
|
break;
|
|
case 2:
|
|
item.fpdSettings.behavior = 1;
|
|
item.fpdSettings.notifications = 1;
|
|
break;
|
|
case 3:
|
|
item.fpdSettings.behavior = 2;
|
|
item.fpdSettings.notifications = 1;
|
|
}
|
|
}
|
|
item.version = 6.6;
|
|
}
|
|
if (item.version < 6.7) {
|
|
await browser.storage.sync.remove("whitelistedHosts"); // Renamed in 6.2 but not removed
|
|
item.version = 6.7;
|
|
}
|
|
if (item.version < 6.8) {
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (l.webworker === 2) {
|
|
let score = 0;
|
|
for (group of ["time_precision", "net", "geolocation", "physical_environment", "useridle", "coopschedule", "gamepads", "vr", "analytics", "nfc"]) {
|
|
if (l[group] !== undefined) {
|
|
score++;
|
|
}
|
|
}
|
|
for (group of ["htmlcanvaselement", "audiobuffer", "webgl", "hardware"]) {
|
|
if (l[group] === 1) {
|
|
score--;
|
|
}
|
|
else if (l[group] > 1) {
|
|
score++;
|
|
}
|
|
}
|
|
for (group of ["enumerateDevices", "plugins"]) {
|
|
if (l[group] === 1 || l[group] === 2) {
|
|
score--;
|
|
}
|
|
else if (l[group] > 2) {
|
|
score++;
|
|
}
|
|
}
|
|
if (score > 8) { // L0: 0, L1: 10, L2: 4, L3: 16
|
|
l.webworker = 3;
|
|
}
|
|
}
|
|
}
|
|
item.version = 6.8;
|
|
}
|
|
if (item.version < 7) {
|
|
for (level in item["custom_levels"]) {
|
|
let l = item["custom_levels"][level];
|
|
if (l.audiobuffer === 1 || l.htmlcanvaselement === 1) {
|
|
l.wasm = 1;
|
|
}
|
|
}
|
|
item.nbsWhitelist[defaultNbsWhitelistDomain] = true;
|
|
item.version = 7;
|
|
}
|
|
|
|
|
|
|
|
await browser.storage.sync.set(item);
|
|
// origin of update.js must be recognized (background script vs. options page)
|
|
if (typeof fpdLoadConfiguration === "function") {
|
|
fpdLoadConfiguration();
|
|
}
|
|
else {
|
|
browser.runtime.sendMessage({purpose: "fpd-load-config"});
|
|
}
|
|
if (typeof nbsLoadConfiguration === "function") {
|
|
nbsLoadConfiguration();
|
|
}
|
|
else {
|
|
browser.runtime.sendMessage({purpose: "nbs-load-config"})
|
|
}
|
|
}
|
|
browser.runtime.onInstalled.addListener(installUpdate);
|
|
// fallback - populate storage with valid data (if onInstalled won't fire)
|
|
browser.storage.sync.get(null).then((item) => {
|
|
checkAndSaveConfig(item, false); // level might not be loaded by this time
|
|
});
|
|
|
|
async function checkAndSaveConfig(conf, check_default = true) {
|
|
let checkSettingRange = (module, setting, range, defValue) => {
|
|
if (!(conf[module][setting] in range)) {
|
|
conf[module][setting] = defValue;
|
|
}
|
|
}
|
|
let checkExistAndType = (name, type, defValue) => {
|
|
if (!(name in conf && typeof(conf[name]) === type)) {
|
|
conf[name] = defValue;
|
|
}
|
|
}
|
|
checkExistAndType("version", "number", 2.1);
|
|
checkExistAndType("requestShieldOn", "boolean", true);
|
|
checkExistAndType("fpDetectionOn", "boolean", false);
|
|
checkExistAndType("custom_levels", "object", {});
|
|
if (!("__default__" in conf) || typeof(conf.__default__) !== "string" ||
|
|
(!(conf.__default__ in levels) && check_default)) {
|
|
conf.__default__ = "2";
|
|
}
|
|
checkExistAndType("domains", "object", {});
|
|
checkExistAndType("nbsWhitelist", "object", defaultNbsWhitelist);
|
|
checkExistAndType("nbsSettings", "object", {});
|
|
checkSettingRange("nbsSettings", "blocking", [0,1], 1);
|
|
checkSettingRange("nbsSettings", "notifications", [0,1], 1);
|
|
checkExistAndType("fpdWhitelist", "object", {});
|
|
checkExistAndType("fpdSettings", "object", {});
|
|
checkSettingRange("fpdSettings", "behavior", [0,1,2,3], 1);
|
|
checkSettingRange("fpdSettings", "notifications", [0,1], 1);
|
|
checkSettingRange("fpdSettings", "detection", [0,1], 0);
|
|
await browser.storage.sync.set(conf);
|
|
await installUpdate();
|
|
}
|