aboutsummaryrefslogtreecommitdiff
path: root/main_background.js
diff options
context:
space:
mode:
Diffstat (limited to 'main_background.js')
-rw-r--r--main_background.js427
1 files changed, 192 insertions, 235 deletions
diff --git a/main_background.js b/main_background.js
index 0bfb33c..d3d7bb4 100644
--- a/main_background.js
+++ b/main_background.js
@@ -20,15 +20,15 @@
* along with GNU LibreJS. If not, see <http://www.gnu.org/licenses/>.
*/
-var acorn = require('acorn');
-var acornLoose = require('acorn-loose');
-var legacy_license_lib = require("./legacy_license_check.js");
-var { ResponseProcessor } = require("./bg/ResponseProcessor");
-var { Storage, ListStore, hash } = require("./common/Storage");
-var { ListManager } = require("./bg/ListManager");
-var { ExternalLicenses } = require("./bg/ExternalLicenses");
-
-console.log("main_background.js");
+const acorn = require('acorn');
+const legacy_license_lib = require('./legacy_license_check.js');
+const { ResponseProcessor } = require('./bg/ResponseProcessor');
+const { Storage, ListStore, hash } = require('./common/Storage');
+const { ListManager } = require('./bg/ListManager');
+const { ExternalLicenses } = require('./bg/ExternalLicenses');
+const { licenses } = require('./license_definitions');
+
+console.log('main_background.js');
/**
* If this is true, it evaluates entire scripts instead of returning as soon as it encounters a violation.
*
@@ -41,7 +41,7 @@ var time = Date.now();
function dbg_print(a, b) {
if (PRINT_DEBUG == true) {
- console.log("Time spent so far: " + (Date.now() - time) / 1000 + " seconds");
+ console.log('Time spent so far: ' + (Date.now() - time) / 1000 + ' seconds');
if (b === undefined) {
console.log(a);
} else {
@@ -64,24 +64,18 @@ function dbg_print(a, b) {
- "// @license [magnet link] [identifier]" then "// @license-end" (may also use /* comments)
- Automatic whitelist: (http://bzr.savannah.gnu.org/lh/librejs/dev/annotate/head:/data/script_libraries/script-libraries.json_
*/
-const { licenses } = require("./license_definitions");
// These are objects that it will search for in an initial regex pass over non-free scripts.
var reserved_objects = [
//"document",
//"window",
- "fetch",
- "XMLHttpRequest",
- "chrome", // only on chrome
- "browser", // only on firefox
- "eval"
+ 'fetch',
+ 'XMLHttpRequest',
+ 'chrome', // only on chrome
+ 'browser', // only on firefox
+ 'eval'
];
-// Generates JSON key for local storage
-function get_storage_key(script_name, src_hash) {
- return script_name;
-}
-
/*
*
* Called when something changes the persistent data of the add-on.
@@ -95,21 +89,13 @@ function get_storage_key(script_name, src_hash) {
*
*/
function options_listener(changes, area) {
- // The cache must be flushed when settings are changed
- // TODO: See if this can be minimized
- function flushed() {
- dbg_print("cache flushed");
- }
- //var flushingCache = browser.webRequest.handlerBehaviorChanged(flushed);
-
-
- dbg_print("Items updated in area" + area + ": ");
+ dbg_print('Items updated in area' + area + ': ');
var changedItems = Object.keys(changes);
- var changed_items = "";
+ var changed_items = '';
for (var i = 0; i < changedItems.length; i++) {
var item = changedItems[i];
- changed_items += item + ",";
+ changed_items += item + ',';
}
dbg_print(changed_items);
@@ -120,21 +106,21 @@ var activeMessagePorts = {};
var activityReports = {};
async function createReport(initializer) {
if (!(initializer && (initializer.url || initializer.tabId))) {
- throw new Error("createReport() needs an URL or a tabId at least");
+ throw new Error('createReport() needs an URL or a tabId at least');
}
let template = {
- "accepted": [],
- "blocked": [],
- "blacklisted": [],
- "whitelisted": [],
- "unknown": [],
+ 'accepted': [],
+ 'blocked': [],
+ 'blacklisted': [],
+ 'whitelisted': [],
+ 'unknown': [],
};
template = Object.assign(template, initializer);
- let [url] = (template.url || (await browser.tabs.get(initializer.tabId)).url).split("#");
+ let [url] = (template.url || (await browser.tabs.get(initializer.tabId)).url).split('#');
template.url = url;
template.site = ListStore.siteItem(url);
template.siteStatus = listManager.getStatus(template.site);
- let list = { "whitelisted": whitelist, "blacklisted": blacklist }[template.siteStatus];
+ let list = { 'whitelisted': whitelist, 'blacklisted': blacklist }[template.siteStatus];
if (list) {
template.listedSite = ListManager.siteMatch(template.site, list);
}
@@ -159,7 +145,7 @@ async function openReportInTab(data) {
*/
function debug_delete_local() {
browser.storage.local.clear();
- dbg_print("Local storage cleared");
+ dbg_print('Local storage cleared');
}
/**
@@ -169,12 +155,12 @@ function debug_delete_local() {
*/
function debug_print_local() {
function storage_got(items) {
- console.log("%c Local storage: ", 'color: red;');
+ console.log('%c Local storage: ', 'color: red;');
for (var i in items) {
- console.log("%c " + i + " = " + items[i], 'color: blue;');
+ console.log('%c ' + i + ' = ' + items[i], 'color: blue;');
}
}
- console.log("%c Variable 'activityReports': ", 'color: red;');
+ console.log('%c Variable \'activityReports\': ', 'color: red;');
console.log(activityReports);
browser.storage.local.get(storage_got);
}
@@ -200,7 +186,7 @@ async function updateReport(tabId, oldReport, updateUI = false) {
for (let property of Object.keys(oldReport)) {
let entries = oldReport[property];
if (!Array.isArray(entries)) continue;
- let defValue = property === "accepted" || property === "blocked" ? property : "unknown";
+ 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);
@@ -235,20 +221,20 @@ async function updateReport(tabId, oldReport, updateUI = false) {
* Make sure it will use the right URL when refering to a certain script.
*
*/
-async function addReportEntry(tabId, scriptHashOrUrl, action) {
+async function addReportEntry(tabId, action) {
let report = activityReports[tabId];
if (!report) report = activityReports[tabId] =
await createReport({ tabId });
let type, actionValue;
- for (type of ["accepted", "blocked", "whitelisted", "blacklisted"]) {
+ for (type of ['accepted', 'blocked', 'whitelisted', 'blacklisted']) {
if (type in action) {
actionValue = action[type];
break;
}
}
if (!actionValue) {
- console.debug("Something wrong with action", action);
- return "";
+ console.debug('Something wrong with action', action);
+ return '';
}
// Search unused data for the given entry
@@ -271,15 +257,12 @@ async function addReportEntry(tabId, scriptHashOrUrl, action) {
entries.push(actionValue);
}
} catch (e) {
- console.error("action %o, type %s, entryType %s", action, type, entryType, e);
- entryType = "unknown";
+ console.error('action %o, type %s, entryType %s', action, type, entryType, e);
+ entryType = 'unknown';
}
if (activeMessagePorts[tabId]) {
- try {
- activeMessagePorts[tabId].postMessage({ show_info: report });
- } catch (e) {
- }
+ activeMessagePorts[tabId].postMessage({ show_info: report });
}
if (browser.sessions) browser.sessions.setTabValue(tabId, report.url, report);
@@ -290,14 +273,14 @@ async function addReportEntry(tabId, scriptHashOrUrl, action) {
function get_domain(url) {
var domain = url.replace('http://', '').replace('https://', '').split(/[/?#]/)[0];
- if (url.indexOf("http://") == 0) {
- domain = "http://" + domain;
+ if (url.indexOf('http://') == 0) {
+ domain = 'http://' + domain;
}
- else if (url.indexOf("https://") == 0) {
- domain = "https://" + domain;
+ else if (url.indexOf('https://') == 0) {
+ domain = 'https://' + domain;
}
- domain = domain + "/";
- domain = domain.replace(/ /g, "");
+ domain = domain + '/';
+ domain = domain.replace(/ /g, '');
return domain;
}
@@ -306,26 +289,25 @@ function get_domain(url) {
* This is the callback where the content scripts of the browser action will contact the background script.
*
*/
-var portFromCS;
async function connected(p) {
- if (p.name === "contact_finder") {
+ if (p.name === 'contact_finder') {
// style the contact finder panel
await browser.tabs.insertCSS(p.sender.tab.id, {
- file: "/content/dialog.css",
- cssOrigin: "user",
+ file: '/content/dialog.css',
+ cssOrigin: 'user',
matchAboutBlank: true,
allFrames: true
});
// Send a message back with the relevant settings
- p.postMessage(await browser.storage.local.get(["prefs_subject", "prefs_body"]));
+ p.postMessage(await browser.storage.local.get(['prefs_subject', 'prefs_body']));
return;
}
p.onMessage.addListener(async function(m) {
var update = false;
var contact_finder = false;
- for (let action of ["whitelist", "blacklist", "forget"]) {
+ for (let action of ['whitelist', 'blacklist', 'forget']) {
if (m[action]) {
let [key] = m[action];
if (m.site) {
@@ -342,18 +324,18 @@ async function connected(p) {
openReportInTab(m.report_tab);
}
// a debug feature
- if (m["printlocalstorage"] !== undefined) {
- console.log("Print local storage");
+ if (m['printlocalstorage'] !== undefined) {
+ console.log('Print local storage');
debug_print_local();
}
// invoke_contact_finder
- if (m["invoke_contact_finder"] !== undefined) {
+ if (m['invoke_contact_finder'] !== undefined) {
contact_finder = true;
await injectContactFinder();
}
// a debug feature (maybe give the user an option to do this?)
- if (m["deletelocalstorage"] !== undefined) {
- console.log("Delete local storage");
+ if (m['deletelocalstorage'] !== undefined) {
+ console.log('Delete local storage');
debug_delete_local();
}
@@ -365,8 +347,8 @@ async function connected(p) {
//inject_contact_finder(tabs[0]["id"]);
}
if (update || m.update && activityReports[m.tabId]) {
- let tabId = "tabId" in m ? m.tabId : tabs.pop().id;
- dbg_print(`%c updating tab ${tabId}`, "color: red;");
+ let tabId = 'tabId' in m ? m.tabId : tabs.pop().id;
+ dbg_print(`%c updating tab ${tabId}`, 'color: red;');
activeMessagePorts[tabId] = p;
await updateReport(tabId, activityReports[tabId], true);
} else {
@@ -374,10 +356,10 @@ async function connected(p) {
if (activityReports[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": activityReports[tab.id] });
+ p.postMessage({ 'show_info': activityReports[tab.id] });
} else {
// create a new entry
- let report = activityReports[tab.id] = await createReport({ "url": tab.url, tabId: tab.id });
+ let report = activityReports[tab.id] = await createReport({ 'url': tab.url, tabId: tab.id });
p.postMessage({ show_info: report });
dbg_print(`[TABID: ${tab.id}] No data found, creating a new entry for this window.`);
}
@@ -392,8 +374,8 @@ async function connected(p) {
* Delete the info we are storing about this tab if there is any.
*
*/
-function delete_removed_tab_info(tab_id, remove_info) {
- dbg_print("[TABID:" + tab_id + "]" + "Deleting stored info about closed tab");
+function delete_removed_tab_info(tab_id, _) {
+ dbg_print('[TABID:' + tab_id + ']' + 'Deleting stored info about closed tab');
if (activityReports[tab_id] !== undefined) {
delete activityReports[tab_id];
}
@@ -411,8 +393,8 @@ function delete_removed_tab_info(tab_id, remove_info) {
*
*/
-async function onTabUpdated(tabId, changedInfo, tab) {
- let [url] = tab.url.split("#");
+async function onTabUpdated(tabId, _, tab) {
+ let [url] = tab.url.split('#');
let report = activityReports[tabId];
if (!(report && report.url === url)) {
let cache = browser.sessions &&
@@ -429,53 +411,35 @@ async function onTabActivated({ tabId }) {
/* *********************************************************************************************** */
-var fname_data = require("./fname_data.json").fname_data;
+var fname_data = require('./fname_data.json').fname_data;
//************************this part can be tested in the HTML file index.html's script test.js****************************
function full_evaluate(script) {
- var res = true;
- if (script === undefined || script == "") {
- return [true, "Harmless null script"];
+ if (script === undefined || script == '') {
+ return [true, 'Harmless null script'];
}
- var ast = acornLoose.parse(script).body[0];
-
- var flag = false;
var amtloops = 0;
- var loopkeys = { "for": true, "if": true, "while": true, "switch": true };
- var operators = { "||": true, "&&": true, "=": true, "==": true, "++": true, "--": true, "+=": true, "-=": true, "*": true };
+ var loopkeys = { 'for': true, 'if': true, 'while': true, 'switch': true };
+ var operators = { '||': true, '&&': true, '=': true, '==': true, '++': true, '--': true, '+=': true, '-=': true, '*': true };
try {
var tokens = acorn.tokenizer(script);
} catch (e) {
- console.warn("Tokenizer could not be initiated (probably invalid code)");
- return [false, "Tokenizer could not be initiated (probably invalid code)"];
+ console.warn('Tokenizer could not be initiated (probably invalid code)');
+ return [false, 'Tokenizer could not be initiated (probably invalid code)'];
}
try {
var toke = tokens.getToken();
} catch (e) {
console.log(script);
console.log(e);
- console.warn("couldn't get first token (probably invalid code)");
- console.warn("Continuing evaluation");
+ console.warn('couldn\'t get first token (probably invalid code)');
+ console.warn('Continuing evaluation');
}
/**
- * Given the end of an identifer token, it tests for bracket suffix notation
- */
- function being_called(end) {
- var i = 0;
- while (script.charAt(end + i).match(/\s/g) !== null) {
- i++;
- if (i >= script.length - 1) {
- return false;
- }
- }
-
- return script.charAt(end + i) == "(";
- }
- /**
* Given the end of an identifer token, it tests for parentheses
*/
function is_bsn(end) {
@@ -486,9 +450,8 @@ function full_evaluate(script) {
return false;
}
}
- return script.charAt(end + i) == "[";
+ return script.charAt(end + i) == '[';
}
- var error_count = 0;
var defines_functions = false;
while (toke !== undefined && toke.type != acorn.tokTypes.eof) {
if (toke.type.keyword !== undefined) {
@@ -497,17 +460,17 @@ function full_evaluate(script) {
// This type of loop detection ignores functional loop alternatives and ternary operators
- if (toke.type.keyword == "function") {
- dbg_print("%c NOTICE: Function declaration.", "color:green");
+ if (toke.type.keyword == 'function') {
+ dbg_print('%c NOTICE: Function declaration.', 'color:green');
defines_functions = true;
}
if (loopkeys[toke.type.keyword] !== undefined) {
amtloops++;
if (amtloops > 3) {
- dbg_print("%c NONTRIVIAL: Too many loops/conditionals.", "color:red");
+ dbg_print('%c NONTRIVIAL: Too many loops/conditionals.', 'color:red');
if (DEBUG == false) {
- return [false, "NONTRIVIAL: Too many loops/conditionals."];
+ return [false, 'NONTRIVIAL: Too many loops/conditionals.'];
}
}
}
@@ -517,44 +480,44 @@ function full_evaluate(script) {
} else if (toke.value !== undefined) {
var status = fname_data[toke.value];
if (status === true) { // is the identifier banned?
- dbg_print("%c NONTRIVIAL: nontrivial token: '" + toke.value + "'", "color:red");
+ dbg_print('%c NONTRIVIAL: nontrivial token: \'' + toke.value + '\'', 'color:red');
if (DEBUG == false) {
- return [false, "NONTRIVIAL: nontrivial token: '" + toke.value + "'"];
+ return [false, 'NONTRIVIAL: nontrivial token: \'' + toke.value + '\''];
}
} else if (status === false) {// is the identifier not banned?
// Is there bracket suffix notation?
if (is_bsn(toke.end)) {
- dbg_print("%c NONTRIVIAL: Bracket suffix notation on variable '" + toke.value + "'", "color:red");
+ dbg_print('%c NONTRIVIAL: Bracket suffix notation on variable \'' + toke.value + '\'', 'color:red');
if (DEBUG == false) {
- return [false, "%c NONTRIVIAL: Bracket suffix notation on variable '" + toke.value + "'"];
+ return [false, '%c NONTRIVIAL: Bracket suffix notation on variable \'' + toke.value + '\''];
}
}
} else if (status === undefined) {// is the identifier user defined?
// Is there bracket suffix notation?
if (is_bsn(toke.end)) {
- dbg_print("%c NONTRIVIAL: Bracket suffix notation on variable '" + toke.value + "'", "color:red");
+ dbg_print('%c NONTRIVIAL: Bracket suffix notation on variable \'' + toke.value + '\'', 'color:red');
if (DEBUG == false) {
- return [false, "NONTRIVIAL: Bracket suffix notation on variable '" + toke.value + "'"];
+ return [false, 'NONTRIVIAL: Bracket suffix notation on variable \'' + toke.value + '\''];
}
}
} else {
- dbg_print("trivial token:" + toke.value);
+ dbg_print('trivial token:' + toke.value);
}
}
// If not a keyword or an identifier it's some kind of operator, field parenthesis, brackets
try {
toke = tokens.getToken();
} catch (e) {
- dbg_print("Denied script because it cannot be parsed.");
- return [false, "NONTRIVIAL: Cannot be parsed. This could mean it is a 404 error."];
+ dbg_print('Denied script because it cannot be parsed.');
+ return [false, 'NONTRIVIAL: Cannot be parsed. This could mean it is a 404 error.'];
}
}
- dbg_print("%cAppears to be trivial.", "color:green;");
+ dbg_print('%cAppears to be trivial.', 'color:green;');
if (defines_functions === true)
- return [true, "Script appears to be trivial but defines functions."];
+ return [true, 'Script appears to be trivial but defines functions.'];
else
- return [true, "Script appears to be trivial."];
+ return [true, 'Script appears to be trivial.'];
}
@@ -573,35 +536,31 @@ function full_evaluate(script) {
*/
function evaluate(script, name) {
function reserved_object_regex(object) {
- var arith_operators = "\\+\\-\\*\\/\\%\\=";
- var scope_chars = "\{\}\]\[\(\)\,";
- var trailing_chars = "\s*" + "\(\.\[";
- return new RegExp("(?:[^\\w\\d]|^|(?:" + arith_operators + "))" + object + '(?:\\s*?(?:[\\;\\,\\.\\(\\[])\\s*?)', "g");
+ var arith_operators = '\\+\\-\\*\\/\\%\\=';
+ return new RegExp('(?:[^\\w\\d]|^|(?:' + arith_operators + '))' + object + '(?:\\s*?(?:[\\;\\,\\.\\(\\[])\\s*?)', 'g');
}
- reserved_object_regex("window");
- var all_strings = new RegExp('".*?"' + "|'.*?'", "gm");
+ reserved_object_regex('window');
var ml_comment = /\/\*([\s\S]+?)\*\//g;
var il_comment = /\/\/.+/gm;
- var bracket_pairs = /\[.+?\]/g;
- var temp = script.replace(/'.+?'+/gm, "'string'");
+ var temp = script.replace(/'.+?'+/gm, '\'string\'');
temp = temp.replace(/".+?"+/gm, '"string"');
- temp = temp.replace(ml_comment, "");
- temp = temp.replace(il_comment, "");
- dbg_print("%c ------evaluation results for " + name + "------", "color:white");
- dbg_print("Script accesses reserved objects?");
+ temp = temp.replace(ml_comment, '');
+ temp = temp.replace(il_comment, '');
+ dbg_print('%c ------evaluation results for ' + name + '------', 'color:white');
+ dbg_print('Script accesses reserved objects?');
var flag = true;
- var reason = ""
+ var reason = ''
// This is where individual "passes" are made over the code
for (var i = 0; i < reserved_objects.length; i++) {
var res = reserved_object_regex(reserved_objects[i]).exec(temp);
if (res != null) {
- dbg_print("%c fail", "color:red;");
+ dbg_print('%c fail', 'color:red;');
flag = false;
- reason = "Script uses a reserved object (" + reserved_objects[i] + ")";
+ reason = 'Script uses a reserved object (' + reserved_objects[i] + ')';
}
}
if (flag) {
- dbg_print("%c pass", "color:green;");
+ dbg_print('%c pass', 'color:green;');
} else {
return [flag, reason];
}
@@ -612,15 +571,15 @@ function evaluate(script, name) {
function validateLicense(matches) {
if (!(Array.isArray(matches) && matches.length >= 4)) {
- return [false, "Malformed or unrecognized license tag."];
+ return [false, 'Malformed or unrecognized license tag.'];
}
- const [all, first] = [matches[0], matches[2].replace("&amp;", "&")];
+ const [all, first] = [matches[0], matches[2].replace('&amp;', '&')];
for (const key in licenses) {
// Match by link on first parameter (legacy)
for (const url of licenses[key].canonicalUrl) {
- if (first === url || first === url.replace("^http://", "https://")) {
+ if (first === url || first === url.replace('^http://', 'https://')) {
return [true, `Recognized license: "${licenses[key].licenseName}".`];
}
}
@@ -647,12 +606,12 @@ function license_read(scriptSrc, name, external = false) {
return [true, scriptSrc, `Licensed under: ${license}`];
}
if (listManager.builtInHashes.has(hash(scriptSrc))) {
- return [true, scriptSrc, "Common script known to be free software."];
+ return [true, scriptSrc, 'Common script known to be free software.'];
}
- let editedSrc = "";
+ let editedSrc = '';
let uneditedSrc = scriptSrc.trim();
- let reason = uneditedSrc ? "" : "Empty source.";
+ let reason = uneditedSrc ? '' : 'Empty source.';
let partsDenied = false;
let partsAccepted = false;
@@ -661,14 +620,14 @@ function license_read(scriptSrc, name, external = false) {
return true; // empty, ignore it
}
const [trivial, message] = external ?
- [false, "External script with no known license"]
+ [false, 'External script with no known license']
: evaluate(s, name);
if (trivial) {
partsAccepted = true;
editedSrc += s;
} else {
partsDenied = true;
- if (s.startsWith("javascript:"))
+ if (s.startsWith('javascript:'))
editedSrc += `# LIBREJS BLOCKED: ${message}`;
else
editedSrc += `/*\nLIBREJS BLOCKED: ${message}\n*/`;
@@ -678,7 +637,7 @@ function license_read(scriptSrc, name, external = false) {
}
while (uneditedSrc) {
- const openingMatch = /\/[\/\*]\s*?(@license)\s+(\S+)\s+(\S+).*$/mi.exec(uneditedSrc);
+ const openingMatch = /\/[/*]\s*?(@license)\s+(\S+)\s+(\S+).*$/mi.exec(uneditedSrc);
if (!openingMatch) { // no license found, check for triviality
checkTriviality(uneditedSrc);
break;
@@ -694,12 +653,12 @@ function license_read(scriptSrc, name, external = false) {
const closureMatch = /\/([*/])\s*@license-end\b[^*/\n]*/i.exec(uneditedSrc);
if (!closureMatch) {
- const msg = "ERROR: @license with no @license-end";
+ const msg = 'ERROR: @license with no @license-end';
return [false, `\n/*\n ${msg} \n*/\n`, msg];
}
- const closureEndIndex = closureMatch.index + closureMatch[0].length;
- const commentEndOffset = uneditedSrc.substring(closureEndIndex).indexOf(closureMatch[1] === "*" ? "*/" : "\n");
+ let closureEndIndex = closureMatch.index + closureMatch[0].length;
+ const commentEndOffset = uneditedSrc.substring(closureEndIndex).indexOf(closureMatch[1] === '*' ? '*/' : '\n');
if (commentEndOffset !== -1) {
closureEndIndex += commentEndOffset;
}
@@ -741,17 +700,17 @@ async function get_script(response, url, tabId = -1, whitelisted = false, index
}
- let scriptName = url.split("/").pop();
+ let scriptName = url.split('/').pop();
if (whitelisted) {
if (tabId !== -1) {
let site = ListManager.siteMatch(url, whitelist);
// Accept without reading script, it was explicitly whitelisted
let reason = site
? `All ${site} whitelisted by user`
- : "Address whitelisted by user";
- addReportEntry(tabId, url, { "whitelisted": [site || url, reason], url });
+ : 'Address whitelisted by user';
+ addReportEntry(tabId, { 'whitelisted': [site || url, reason], url });
}
- if (response.startsWith("javascript:"))
+ if (response.startsWith('javascript:'))
return result(response);
else
return result(`/* LibreJS: script whitelisted by user preference. */\n${response}`);
@@ -763,25 +722,27 @@ async function get_script(response, url, tabId = -1, whitelisted = false, index
return result(verdict ? response : editedSource);
}
- let sourceHash = hash(response);
let domain = get_domain(url);
let report = activityReports[tabId] || (activityReports[tabId] = await createReport({ tabId }));
updateBadge(tabId, report, !verdict);
- let category = await addReportEntry(tabId, sourceHash, { "url": domain, [verdict ? "accepted" : "blocked"]: [url, reason] });
+ let category = await addReportEntry(tabId, { 'url': domain, [verdict ? 'accepted' : 'blocked']: [url, reason] });
switch (category) {
- case "blacklisted":
+ case 'blacklisted': {
editedSource = `/* LibreJS: script ${category} by user. */`;
- return result(response.startsWith("javascript:")
+ return result(response.startsWith('javascript:')
? `javascript:void(${encodeURIComponent(editedSource)})` : editedSource);
- case "whitelisted":
- return result(response.startsWith("javascript:")
+ }
+ case 'whitelisted': {
+ return result(response.startsWith('javascript:')
? response : `/* LibreJS: script ${category} by user. */\n${response}`);
- default:
+ }
+ default: {
let scriptSource = verdict ? response : editedSource;
- return result(response.startsWith("javascript:")
+ return result(response.startsWith('javascript:')
? (verdict ? scriptSource : `javascript:void(/* ${scriptSource} */)`)
: `/* LibreJS: script ${category}. */\n${scriptSource}`
);
+ }
}
}
@@ -789,9 +750,9 @@ async function get_script(response, url, tabId = -1, whitelisted = false, index
function updateBadge(tabId, report = null, forceRed = false) {
let blockedCount = report ? report.blocked.length + report.blacklisted.length : 0;
let [text, color] = blockedCount > 0 || forceRed
- ? [blockedCount && blockedCount.toString() || "!", "red"] : ["✓", "green"]
+ ? [blockedCount && blockedCount.toString() || '!', 'red'] : ['✓', 'green']
let { browserAction } = browser;
- if ("setBadgeText" in browserAction) {
+ if ('setBadgeText' in browserAction) {
browserAction.setBadgeText({ text, tabId });
browserAction.setBadgeBackgroundColor({ color, tabId });
} else {
@@ -815,11 +776,11 @@ async function blockBlacklistedScripts(request) {
let { url, tabId, documentUrl } = request;
url = ListStore.urlItem(url);
let status = listManager.getStatus(url);
- if (status !== "blacklisted") return {};
+ if (status !== 'blacklisted') return {};
let blacklistedSite = ListManager.siteMatch(url, blacklist);
- await addReportEntry(tabId, url, {
+ await addReportEntry(tabId, {
url: documentUrl,
- "blacklisted": [url, /\*/.test(blacklistedSite) ? `User blacklisted ${blacklistedSite}` : "Blacklisted by user"]
+ 'blacklisted': [url, /\*/.test(blacklistedSite) ? `User blacklisted ${blacklistedSite}` : 'Blacklisted by user']
});
return { cancel: true };
}
@@ -844,36 +805,36 @@ var ResponseHandler = {
let blacklistedSite = ListManager.siteMatch(site, blacklist);
let blacklisted = blacklistedSite || blacklist.contains(url);
- let topUrl = type === "sub_frame" && request.frameAncestors && request.frameAncestors.pop() || documentUrl;
+ let topUrl = type === 'sub_frame' && request.frameAncestors && request.frameAncestors.pop() || documentUrl;
if (blacklisted) {
- if (type === "script") {
+ if (type === 'script') {
// this shouldn't happen, because we intercept earlier in blockBlacklistedScripts()
return ResponseProcessor.REJECT;
}
- if (type === "main_frame") { // we handle the page change here too, since we won't call edit_html()
+ if (type === 'main_frame') { // we handle the page change here too, since we won't call edit_html()
activityReports[tabId] = await createReport({ url: fullUrl, tabId });
// Go on without parsing the page: it was explicitly blacklisted
let reason = blacklistedSite
? `All ${blacklistedSite} blacklisted by user`
- : "Address blacklisted by user";
- await addReportEntry(tabId, url, { "blacklisted": [blacklistedSite || url, reason], url: fullUrl });
+ : 'Address blacklisted by user';
+ await addReportEntry(tabId, { 'blacklisted': [blacklistedSite || url, reason], url: fullUrl });
}
// use CSP to restrict JavaScript execution in the page
request.responseHeaders.unshift({
- name: `Content-security-policy`,
- value: `script-src 'none';`
+ name: 'Content-security-policy',
+ value: 'script-src \'none\';'
});
return { responseHeaders: request.responseHeaders }; // let's skip the inline script parsing, since we block by CSP
} else {
let whitelistedSite = ListManager.siteMatch(site, whitelist);
let whitelisted = response.whitelisted = whitelistedSite || whitelist.contains(url);
- if (type === "script") {
+ if (type === 'script') {
if (whitelisted) {
// accept the script and stop processing
- addReportEntry(tabId, url, {
+ addReportEntry(tabId, {
url: topUrl,
- "whitelisted": [url, whitelistedSite ? `User whitelisted ${whitelistedSite}` : "Whitelisted by user"]
+ 'whitelisted': [url, whitelistedSite ? `User whitelisted ${whitelistedSite}` : 'Whitelisted by user']
});
return ResponseProcessor.ACCEPT;
} else {
@@ -882,13 +843,13 @@ var ResponseHandler = {
let verdict, ret;
let msg = scriptInfo.toString();
if (scriptInfo.free) {
- verdict = "accepted";
+ verdict = 'accepted';
ret = ResponseProcessor.ACCEPT;
} else {
- verdict = "blocked";
+ verdict = 'blocked';
ret = ResponseProcessor.REJECT;
}
- addReportEntry(tabId, url, { url, [verdict]: [url, msg] });
+ addReportEntry(tabId, { url, [verdict]: [url, msg] });
return ret;
}
}
@@ -904,7 +865,7 @@ var ResponseHandler = {
*/
async post(response) {
let { type } = response.request;
- let handle_it = type === "script" ? handle_script : handle_html;
+ let handle_it = type === 'script' ? handle_script : handle_html;
return await handle_it(response, response.whitelisted);
}
}
@@ -914,7 +875,7 @@ var ResponseHandler = {
*/
async function handle_script(response, whitelisted) {
let { text, request } = response;
- let { url, tabId, frameId } = request;
+ let { url, tabId } = request;
url = ListStore.urlItem(url);
let edited = await get_script(text, url, tabId, whitelisted, -2);
return Array.isArray(edited) ? edited[0] : edited;
@@ -928,7 +889,7 @@ function doc2HTML(doc) {
let s = doc.documentElement.outerHTML;
if (doc.doctype) {
let dt = doc.doctype;
- let sDoctype = `<!DOCTYPE ${dt.name || "html"}`;
+ let sDoctype = `<!DOCTYPE ${dt.name || 'html'}`;
if (dt.publicId) sDoctype += ` PUBLIC "${dt.publicId}"`;
if (dt.systemId) sDoctype += ` "${dt.systemId}"`;
s = `${sDoctype}>\n${s}`;
@@ -940,7 +901,7 @@ function doc2HTML(doc) {
* Shortcut to create a correctly namespaced DOM HTML elements
*/
function createHTMLElement(doc, name) {
- return doc.createElementNS("http://www.w3.org/1999/xhtml", name);
+ return doc.createElementNS('http://www.w3.org/1999/xhtml', name);
}
/**
@@ -948,7 +909,7 @@ function createHTMLElement(doc, name) {
* NOSCRIPT elements to visible the same way as NoScript and uBlock do)
*/
function forceElement(doc, element) {
- let replacement = createHTMLElement(doc, "span");
+ let replacement = createHTMLElement(doc, 'span');
replacement.innerHTML = element.innerHTML;
element.replaceWith(replacement);
return replacement;
@@ -962,12 +923,11 @@ function forceElement(doc, element) {
function forceNoscriptElements(doc) {
let shown = 0;
// inspired by NoScript's onScriptDisabled.js
- for (let noscript of doc.querySelectorAll("noscript:not([data-librejs-nodisplay])")) {
+ for (let noscript of doc.querySelectorAll('noscript:not([data-librejs-nodisplay])')) {
let replacement = forceElement(doc, noscript);
// emulate meta-refresh
let meta = replacement.querySelector('meta[http-equiv="refresh"]');
if (meta) {
- refresh = true;
doc.head.appendChild(meta);
}
shown++;
@@ -981,7 +941,7 @@ function forceNoscriptElements(doc) {
*/
function showConditionalElements(doc) {
let shown = 0;
- for (let element of document.querySelectorAll("[data-librejs-display]")) {
+ for (let element of document.querySelectorAll('[data-librejs-display]')) {
forceElement(doc, element);
shown++;
}
@@ -998,27 +958,27 @@ function read_metadata(meta_element) {
return;
}
- console.log("metadata found");
+ console.log('metadata found');
var metadata = {};
try {
metadata = JSON.parse(meta_element.innerHTML);
} catch (error) {
- console.log("Could not parse metadata on page.")
+ console.log('Could not parse metadata on page.')
return false;
}
- var license_str = metadata["intrinsic-events"];
+ var license_str = metadata['intrinsic-events'];
if (license_str === undefined) {
- console.log("No intrinsic events license");
+ console.log('No intrinsic events license');
return false;
}
console.log(license_str);
- var parts = license_str.split(" ");
+ var parts = license_str.split(' ');
if (parts.length != 2) {
- console.log("invalid (>2 tokens)");
+ console.log('invalid (>2 tokens)');
return false;
}
@@ -1026,15 +986,15 @@ function read_metadata(meta_element) {
parts[0] = parts[0].replace(/&amp;/g, '&');
try {
- for (const url of licenses[part[1]].canonicalUrl) {
- if (url.startsWith("magnet:") && url == parts[0]) {
+ for (const url of licenses[parts[1]].canonicalUrl) {
+ if (url.startsWith('magnet:') && url == parts[0]) {
return true;
}
}
- console.log("invalid (doesn't match licenses)");
+ console.log('invalid (doesn\'t match licenses)');
return false;
} catch (error) {
- console.log("invalid (threw error, key didn't exist)");
+ console.log('invalid (threw error, key didn\'t exist)');
return false;
}
}
@@ -1045,7 +1005,7 @@ function read_metadata(meta_element) {
async function editHtml(html, documentUrl, tabId, frameId, whitelisted) {
var parser = new DOMParser();
- var html_doc = parser.parseFromString(html, "text/html");
+ var html_doc = parser.parseFromString(html, 'text/html');
// moves external licenses reference, if any, before any <SCRIPT> element
ExternalLicenses.optimizeDocument(html_doc, { tabId, frameId, documentUrl });
@@ -1059,8 +1019,8 @@ async function editHtml(html, documentUrl, tabId, frameId, whitelisted) {
var scripts = html_doc.scripts;
- var meta_element = html_doc.getElementById("LibreJS-info");
- var first_script_src = "";
+ var meta_element = html_doc.getElementById('LibreJS-info');
+ var first_script_src = '';
// get the potential inline source that can contain a license
for (let script of scripts) {
@@ -1072,38 +1032,36 @@ async function editHtml(html, documentUrl, tabId, frameId, whitelisted) {
}
let license = false;
- if (first_script_src != "") {
+ if (first_script_src != '') {
license = legacy_license_lib.check(first_script_src);
}
let findLine = finder => finder.test(html) && html.substring(0, finder.lastIndex).split(/\n/).length || 0;
if (read_metadata(meta_element) || license) {
- console.log("Valid license for intrinsic events found");
+ console.log('Valid license for intrinsic events found');
let line, extras;
if (meta_element) {
line = findLine(/id\s*=\s*['"]?LibreJS-info\b/gi);
- extras = "(0)";
+ extras = '(0)';
} else if (license) {
line = html.substring(0, html.indexOf(first_script_src)).split(/\n/).length;
- extras = "\n" + first_script_src;
+ extras = '\n' + first_script_src;
}
- let viewUrl = line ? `view-source:${documentUrl}#line${line}(<${meta_element ? meta_element.tagName : "SCRIPT"}>)${extras}` : url;
- addReportEntry(tabId, url, { url, "accepted": [viewUrl, `Global license for the page: ${license}`] });
+ let viewUrl = line ? `view-source:${documentUrl}#line${line}(<${meta_element ? meta_element.tagName : 'SCRIPT'}>)${extras}` : url;
+ addReportEntry(tabId, { url, 'accepted': [viewUrl, `Global license for the page: ${license}`] });
// Do not process inline scripts
scripts = [];
} else {
let dejaVu = new Map(); // deduplication map & edited script cache
let modified = false;
// Deal with intrinsic events
- let intrinsecindex = 0;
let intrinsicFinder = /<[a-z][^>]*\b(on\w+|href\s*=\s*['"]?javascript:)/gi;
- for (let element of html_doc.all) {
+ for (let element of html_doc.querySelectorAll('*')) {
let line = -1;
for (let attr of element.attributes) {
let { name, value } = attr;
value = value.trim();
- if (name.startsWith("on") || (name === "href" && value.toLowerCase().startsWith("javascript:"))) {
- intrinsecindex++;
+ if (name.startsWith('on') || (name === 'href' && value.toLowerCase().startsWith('javascript:'))) {
if (line === -1) {
line = findLine(intrinsicFinder);
}
@@ -1114,7 +1072,7 @@ async function editHtml(html, documentUrl, tabId, frameId, whitelisted) {
edited = dejaVu.get(key);
} else {
let url = `view-source:${documentUrl}#line${line}(<${element.tagName} ${name}>)\n${value.trim()}`;
- if (name === "href") value = decodeURIComponent(value);
+ if (name === 'href') value = decodeURIComponent(value);
edited = await get_script(value, url, tabId, whitelist.contains(url)); dejaVu.set(key, edited);
}
if (edited && edited !== value) {
@@ -1133,7 +1091,7 @@ async function editHtml(html, documentUrl, tabId, frameId, whitelisted) {
for (let i = 0, len = scripts.length; i < len; i++) {
let script = scripts[i];
let line = findLine(scriptFinder);
- if (!script.src && !(script.type && script.type !== "text/javascript")) {
+ if (!script.src && !(script.type && script.type !== 'text/javascript')) {
let source = script.textContent.trim();
let editedSource;
if (dejaVu.has(source)) {
@@ -1170,18 +1128,18 @@ async function editHtml(html, documentUrl, tabId, frameId, whitelisted) {
async function handle_html(response, whitelisted) {
let { text, request } = response;
let { url, tabId, frameId, type } = request;
- if (type === "main_frame") {
+ if (type === 'main_frame') {
activityReports[tabId] = await createReport({ url, tabId });
updateBadge(tabId);
}
return await editHtml(text, url, tabId, frameId, whitelisted);
}
-var whitelist = new ListStore("pref_whitelist", Storage.CSV);
-var blacklist = new ListStore("pref_blacklist", Storage.CSV);
+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)
+ Object.values(require('./hash_script/whitelist').whitelist)
.reduce((a, b) => a.concat(b)) // as a flat array
.map(script => script.hash)
);
@@ -1189,7 +1147,7 @@ var listManager = new ListManager(whitelist, blacklist,
async function initDefaults() {
let defaults = {
- pref_subject: "Issues with Javascript on your website",
+ pref_subject: 'Issues with Javascript on your website',
pref_body: `Please consider using a free license for the Javascript on your website.
[Message generated by LibreJS. See https://www.gnu.org/software/librejs/ for more information]
@@ -1223,18 +1181,18 @@ async function init_addon() {
browser.tabs.onActivated.addListener(onTabActivated);
// Prevents Google Analytics from being loaded from Google servers
let all_types = [
- "beacon", "csp_report", "font", "image", "imageset", "main_frame", "media",
- "object", "object_subrequest", "ping", "script", "stylesheet", "sub_frame",
- "web_manifest", "websocket", "xbl", "xml_dtd", "xmlhttprequest", "xslt",
- "other"
+ 'beacon', 'csp_report', 'font', 'image', 'imageset', 'main_frame', 'media',
+ 'object', 'object_subrequest', 'ping', 'script', 'stylesheet', 'sub_frame',
+ 'web_manifest', 'websocket', 'xbl', 'xml_dtd', 'xmlhttprequest', 'xslt',
+ 'other'
];
browser.webRequest.onBeforeRequest.addListener(blockGoogleAnalytics,
- { urls: ["<all_urls>"], types: all_types },
- ["blocking"]
+ { urls: ['<all_urls>'], types: all_types },
+ ['blocking']
);
browser.webRequest.onBeforeRequest.addListener(blockBlacklistedScripts,
- { urls: ["<all_urls>"], types: ["script"] },
- ["blocking"]
+ { urls: ['<all_urls>'], types: ['script'] },
+ ['blocking']
);
browser.webRequest.onResponseStarted.addListener(request => {
let { tabId } = request;
@@ -1242,15 +1200,14 @@ async function init_addon() {
if (report) {
updateBadge(tabId, activityReports[tabId]);
}
- }, { urls: ["<all_urls>"], types: ["main_frame"] });
+ }, { urls: ['<all_urls>'], types: ['main_frame'] });
// Analyzes all the html documents and external scripts as they're loaded
ResponseProcessor.install(ResponseHandler);
legacy_license_lib.init();
-
- let Test = require("./common/Test");
+ const Test = require('./common/Test');
if (Test.getURL()) {
// export testable functions to the global scope
this.LibreJS = {
@@ -1260,7 +1217,7 @@ async function init_addon() {
ListManager, ListStore, Storage,
};
// create or focus the autotest tab if it's a debugging session
- if ((await browser.management.getSelf()).installType === "development") {
+ if ((await browser.management.getSelf()).installType === 'development') {
Test.getTab(true);
}
}
@@ -1272,8 +1229,8 @@ async function init_addon() {
*/
async function injectContactFinder(tabId) {
await Promise.all([
- browser.tabs.insertCSS(tabId, { file: "/content/overlay.css", cssOrigin: "user" }),
- browser.tabs.executeScript(tabId, { file: "/content/contactFinder.js" }),
+ browser.tabs.insertCSS(tabId, { file: '/content/overlay.css', cssOrigin: 'user' }),
+ browser.tabs.executeScript(tabId, { file: '/content/contactFinder.js' }),
]);
}