aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhackademix <giorgio@maone.net>2018-07-31 18:08:45 +0200
committerhackademix <giorgio@maone.net>2018-07-31 18:10:42 +0200
commit5975e67b1de999c168209f8a5b7652f3d4551412 (patch)
tree9ddac462cb5c4cde58e521d5d96dec5469c57b98
parent0039b016a8e9d13de5b70e09833aa0cc768350c5 (diff)
Refactoring out list management in its own class / bug fixing and simplifying UI synchronization.
-rw-r--r--bg/ListManager.js71
-rw-r--r--main_background.js264
2 files changed, 140 insertions, 195 deletions
diff --git a/bg/ListManager.js b/bg/ListManager.js
new file mode 100644
index 0000000..34d9531
--- /dev/null
+++ b/bg/ListManager.js
@@ -0,0 +1,71 @@
+/**
+* GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+*
+* Copyright (C) 2018 Giorgio Maone <giorgio@maone.net>
+*
+* This file is part of GNU LibreJS.
+*
+* GNU LibreJS 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.
+*
+* GNU LibreJS 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 GNU LibreJS. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ A class to manage whitelist/blacklist operations
+*/
+
+let {ListStore} = require("./Storage");
+
+class ListManager {
+ constructor(whitelist, blacklist, builtInHashes) {
+ this.lists = {whitelist, blacklist};
+ this.builtInHashes = new Set(builtInHashes);
+ }
+ async whitelist(key) {
+ await this.lists.blacklist.remove(key);
+ await this.lists.whitelist.store(key);
+ }
+ async blacklist(key) {
+ await this.lists.whitelist.remove(key);
+ await this.lists.blacklist.store(key);
+ }
+ async forget(key) {
+ for (let list of Object.values(this.lists)) {
+ await list.remove(key);
+ }
+ }
+ /* key is a string representing either a URL or an optional path
+ with a trailing (hash).
+ Returns "blacklisted", "whitelisted" or defValue
+ */
+ getStatus(key, defValue = "unknown") {
+ let {blacklist, whitelist} = this.lists;
+ let match = key.match(/\(([^)]+)\)(?=[^()]*$)/);
+ if (!match) {
+ let url = ListStore.urlItem(key);
+ let site = ListStore.siteItem(key);
+ return (blacklist.contains(url) || blacklist.contains(site))
+ ? "blacklisted"
+ : whitelist.contains(url) || whitelist.contains(site)
+ ? "whitelisted" : defValue;
+ }
+
+ let [hashItem, srcHash] = match; // (hash), hash
+
+ return blacklist.contains(hashItem) ? "blacklisted"
+ : this.builtInHashes.has(srcHash) || whitelist.contains(hashItem)
+ ? "whitelisted"
+ : defValue;
+ }
+}
+
+module.exports = { ListManager };
diff --git a/main_background.js b/main_background.js
index 108da28..2b2a975 100644
--- a/main_background.js
+++ b/main_background.js
@@ -27,6 +27,7 @@ var walk = require("acorn/dist/walk");
var legacy_license_lib = require("./legacy_license_check.js");
var {ResponseProcessor} = require("./bg/ResponseProcessor");
var {Storage, ListStore} = require("./bg/Storage");
+var {ListManager} = require("./bg/ListManager");
console.log("main_background.js");
/**
@@ -110,15 +111,6 @@ var reserved_objects = [
"eval"
];
-
-// Default whitelist, comes from the script in hash_script
-var wl_data = require("./hash_script/whitelist").whitelist.jquery;
-var default_whitelist = {};
-for(var i = 0; i < wl_data.length; i++){
- default_whitelist[wl_data[i].hash] = true;
-}
-
-
/**
*
* Sets global variable "webex" to either "chrome" or "browser" for
@@ -186,6 +178,7 @@ function createReport(initializer = null) {
"blocked": [],
"blacklisted": [],
"whitelisted": [],
+ "unknown": [],
url: "",
};
return initializer ? Object.assign(template, initializer) : template;
@@ -251,97 +244,24 @@ function debug_print_local(){
* Make sure it will use the right URL when refering to a certain script.
*
*/
-function update_popup(tab_id,blocked_info,update=false){
- var new_blocked_data;
- function get_sto(items){
- //************************************************************************//
- // Move scripts that are accepted/blocked but whitelisted to "whitelisted" category
- // (Ideally, they just would not be tested in the first place because that would be faster)
- var url = blocked_info["url"];
- if(url === undefined){
- console.error("No url passed to update_popup");
- return 1;
- }
-
- function get_status(script_name){
- var temp = script_name.match(/\(.*?\)/g);
- if(temp == null){
- return "none"
- }
- var src_hash = temp[temp.length-1].substr(1,temp[0].length-2);
-
- for(var i in items){
- var res = i.match(/\(.*?\)/g);
- if(res != null){
- var test_hash = res[res.length-1].substr(1,res[0].length-2);
- if(test_hash == src_hash){
- return items[i];
- }
- }
- }
-
- if(default_whitelist[src_hash] !== undefined){
- //console.log("Found script in default whitelist: "+default_whitelist[src_hash]);
- return "whitelist";
- } else{
- //console.log("script " + script_name + " not in default whitelist.");
- return "none";
- }
- }
- function is_bl(script_name){
- if(get_status(script_name) == "blacklist"){
- return true;
- }
- else return false;
- }
- function is_wl(script_name){
- if(get_status(script_name) == "whitelist"){
- return true;
- }
- else return false;
- }
- new_blocked_data = createReport({url});
- for(var type in blocked_info){
- for(var script_arr in blocked_info[type]){
- if(is_bl(blocked_info[type][script_arr][0])){
- new_blocked_data["blacklisted"].push(blocked_info[type][script_arr]);
- //console.log("Script " + blocked_info[type][script_arr][0] + " is blacklisted");
- continue;
- }
- if(is_wl(blocked_info[type][script_arr][0])){
- new_blocked_data["whitelisted"].push(blocked_info[type][script_arr]);
- //console.log("Script " + blocked_info[type][script_arr][0] + " is whitelisted");
- continue;
- }
- if(type == "url"){
- continue;
- }
- // either "blocked" or "accepted"
- new_blocked_data[type].push(blocked_info[type][script_arr]);
- //console.log("Script " + blocked_info[type][script_arr][0] + " isn't whitelisted or blacklisted");
- }
- }
- dbg_print(new_blocked_data);
- //***********************************************************************************************//
- // store the blocked info until it is opened and needed
- if(update == false && active_connections[tab_id] === undefined){
- dbg_print("[TABID:"+tab_id+"]"+"Storing blocked_info for when the browser action is opened or asks for it.");
- if(tab_id == undefined){
- dbg_print("UNDEFINED TAB_ID");
- }
- unused_data[tab_id] = new_blocked_data;
- } else{
- if(tab_id == undefined){
- dbg_print("UNDEFINED TAB_ID");
- }
- unused_data[tab_id] = new_blocked_data;
- dbg_print("[TABID:"+tab_id+"]"+"Sending blocked_info directly to browser action");
- active_connections[tab_id].postMessage({"show_info":new_blocked_data});
- delete active_connections[tab_id];
+function updateReport(tabId, oldReport, updateUI = false){
+ let {url} = oldReport;
+ let newReport = createReport({url});
+ for (let property of Object.keys(oldReport)) {
+ if (property === "url") continue;
+ let entries = oldReport[property];
+ let defValue = property === "accepted" || property === "blocked" ? property : "unknown";
+ for (let script of entries) {
+ let status = listManager.getStatus(script[0], defValue);
+ if (Array.isArray(newReport[status])) newReport[status].push(script);
}
- return 0;
}
- webex.storage.local.get(get_sto);
+ unused_data[tabId] = newReport;
+ dbg_print(newReport);
+ if (updateUI && active_connections[tabId]) {
+ dbg_print(`[TABID: ${tabId}] Sending script blocking report directly to browser action.`);
+ active_connections[tabId].postMessage({show_info: newReport});
+ }
}
/**
@@ -351,14 +271,14 @@ function update_popup(tab_id,blocked_info,update=false){
* Sends a message to the content script that adds a popup entry for a tab.
*
* The action argument is an object with two properties: one named either
-* "accepted","blocked", "whitelisted" or "blacklisted", whose value is the array
-* [scriptName, reason], and another named "url". Example:
+* "accepted","blocked", "whitelisted", "blacklisted" or "unknown", whose value
+* is the array [scriptName, reason], and another named "url". Example:
* action = {
* "accepted": ["jquery.js (someHash)","Whitelisted by user"],
* "url": "https://example.com/js/jquery.js"
* }
*
-* Returns either "wl" (whitelisted), "bl" (blacklisted) or "none".
+* Returns either "wl" (whitelisted), "bl" (blacklisted) or "unknown".
*
* NOTE: This WILL break if you provide inconsistent URLs to it.
* Make sure it will use the right URL when refering to a certain script.
@@ -380,17 +300,6 @@ async function addReportEntry(tabId, scriptHashOrUrl, action, update = false) {
return "";
}
-
- function getStatus(scriptName) {
- let match = scriptName.match(/\(([^)]+)\)(?=[^()]*$)/);
- if (!match) return type;
-
- let [hashItem, srcHash] = match; // (hash), hash
-
- return (blacklist.contains(hashItem)) ? "blacklisted"
- : (default_whitelist[srcHash] || whitelist.contains(hashItem)) ? "whitelisted"
- : type;
- }
// Search unused data for the given entry
function isNew(entries, item) {
for (let e of entries) {
@@ -402,7 +311,7 @@ async function addReportEntry(tabId, scriptHashOrUrl, action, update = false) {
let entryType, res;
let scriptName = actionValue[0];
try {
- entryType = getStatus(scriptName);
+ entryType = listManager.getStatus(scriptName, type);
res = entryType.substring(0, 2);
let entries = unused_data[tabId][entryType];
if(isNew(entries, scriptName)){
@@ -412,8 +321,8 @@ async function addReportEntry(tabId, scriptHashOrUrl, action, update = false) {
entries.push(actionValue);
}
} catch (e) {
- console.error(e, "action %o, type %s, entryType %s", action, type, entryType);
- res = "none";
+ console.error("action %o, type %s, entryType %s", action, type, entryType, e);
+ res = "unknown";
}
return res;
}
@@ -447,52 +356,19 @@ function connected(p) {
webex.storage.local.get(cb);
return;
}
- p.onMessage.addListener(function(m) {
+ p.onMessage.addListener(async function(m) {
console.debug("LibreJS BG: received message", m);
- /**
- * Updates the entry of the current URL in storage
- */
- function set_script(script,val){
- if(val != "whitelist" && val != "forget" && val != "blacklist"){
- console.error("Key must be either 'whitelist', 'blacklist' or 'forget'");
- }
- // (Remember that we do not trust the names of scripts.)
- var current_url = "";
- function geturl(tabs) {
- current_url = tabs[0]["url"];
- var domain = get_domain(current_url);
- var scriptkey = m[val][0];
- switch(val) {
- case "forget":
- whitelist.remove(scriptkey);
- blacklist.remove(scriptkey);
- break;
- case "whitelist":
- blacklist.remove(scriptkey);
- whitelist.store(scriptkey);
- break;
- case "blacklist":
- whitelist.remove(scriptkey);
- blacklist.store(scriptkey);
- break;
- }
- }
- var querying = webex.tabs.query({active: true,currentWindow: true},geturl);
- }
+
var update = false;
var contact_finder = false;
- if(m["whitelist"] !== undefined){
- set_script(m["whitelist"][0],"whitelist");
- update = true;
- }
- if(m["blacklist"] !== undefined){
- set_script(m["blacklist"][0],"blacklist");
- update = true;
- }
- if(m["forget"] !== undefined){
- set_script(m["forget"][0],"forget");
- update = true;
+
+ for (let action of ["whitelist", "blacklist", "forget"]) {
+ if (m[action]) {
+ await listManager[action](m[action][0]);
+ update = true;
+ }
}
+
//
if(m["open_popup_tab"] !== undefined){
open_popup_tab(m["open_popup_tab"]);
@@ -513,42 +389,42 @@ function connected(p) {
debug_delete_local();
}
// Add this domain to the whitelist
- if(m["allow_all"] !== undefined){
- var domain = get_domain(m["allow_all"]["url"]);
- add_csv_whitelist(domain);
+ if(m.allow_all){
+ await listManager.whitelist(ListStore.siteItem(m.allow_all.url));
+ update = true;
}
- // Remote this domain from the whitelist
- if(m["block_all"] !== undefined){
- var domain = get_domain(m["block_all"]["url"]);
- remove_csv_whitelist(domain);
+ // Remove this domain from the whitelist
+ if(m.block_all){
+ await listManager.forget(ListStore.siteItem(m.block_all.url));
+ update = true;
}
- function logTabs(tabs) {
- if(contact_finder){
- dbg_print("[TABID:"+tab_id+"] Injecting contact finder");
- //inject_contact_finder(tabs[0]["id"]);
- }
- if(update){
- dbg_print("%c updating tab "+tabs[0]["id"],"color: red;");
- update_popup(tabs[0]["id"],unused_data[tabs[0]["id"]],true);
- active_connections[tabs[0]["id"]] = p;
- }
- for(var i = 0; i < tabs.length; i++) {
- var tab = tabs[i];
- var tab_id = tab["id"];
- if(unused_data[tab_id] !== undefined){
+
+ let tabs = await browser.tabs.query({active: true, currentWindow: true});
+
+ if(contact_finder){
+ let tab = tabs.pop();
+ dbg_print(`[TABID:${tab.id}] Injecting contact finder`);
+ //inject_contact_finder(tabs[0]["id"]);
+ }
+ if(update){
+ let tab = tabs.pop();
+ dbg_print(`%c updating tab ${tab.id}`, "color: red;");
+ active_connections[tab.id] = p;
+ await updateReport(tab.id, unused_data[tab.id], true);
+ } else {
+ for(let tab of tabs) {
+ if(unused_data[tab.id]){
// If we have some data stored here for this tabID, send it
- dbg_print("[TABID:"+tab_id+"]"+"Sending stored data associated with browser action");
- p.postMessage({"show_info":unused_data[tab_id]});
+ dbg_print(`[TABID: ${tab.id}] Sending stored data associated with browser action'`);
+ p.postMessage({"show_info": unused_data[tab.id]});
} else{
// create a new entry
- unused_data[tab_id] = createReport({"url": tab.url});
- p.postMessage({"show_info":unused_data[tab_id]});
- dbg_print("[TABID:"+tab_id+"]"+"No data found, creating a new entry for this window.");
+ let report = unused_data[tab.id] = createReport({"url": tab.url});
+ p.postMessage({show_info: report});
+ dbg_print(`[TABID: ${tab.id}] No data found, creating a new entry for this window.`);
}
}
}
- var querying = webex.tabs.query({active: true,currentWindow: true},logTabs);
-
});
}
@@ -568,15 +444,6 @@ function delete_removed_tab_info(tab_id, remove_info){
}
}
-/**
-* Check whitelisted by hash
-*
-*/
-function blocked_status(hash) {
- let hashItem = ListStore.hashItem(hash);
- return whitelist.contains(hashItem) ?
- "whitelisted" : blacklist.contains(hashItem) ? "blacklisted" : "none";
-}
/* *********************************************************************************************** */
var fname_data = require("./fname_data.json").fname_data;
@@ -805,7 +672,7 @@ function license_read(script_src, name, external = false){
if(license != false){
return [true,script_src,"Licensed under: "+license];
}
- if (default_whitelist[hash(script_src)]){
+ if (listManager.builtInHashes.has(hash(script_src))){
return [true,script_src,"Common script known to be free software."];
}
while(true){ // TODO: refactor me
@@ -976,6 +843,7 @@ var ResponseHandler = {
let {request} = response;
let {url, type, tabId} = request;
+ url = ListStore.urlItem(url);
let site = ListStore.siteItem(url);
let blacklistedSite = blacklist.contains(site);
@@ -1210,6 +1078,12 @@ async function handle_html(response, whitelisted) {
var whitelist = new ListStore("pref_whitelist", Storage.CSV);
var blacklist = new ListStore("pref_blacklist", Storage.CSV);
+var listManager = new ListManager(whitelist, blacklist,
+ // built-in whitelist of script hashes, e.g. jQuery
+ Object.values(require("./hash_script/whitelist").whitelist)
+ .reduce((a, b) => a.concat(b)) // as a flat array
+ .map(script => script.hash)
+ );
/**
* Initializes various add-on functions