From ae25ad78906179e1448ff7d97957810e2be40206 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Thu, 7 Apr 2022 12:17:38 +1000 Subject: nop whitespace formatting change. - ran eglot-format using typescript-language-server on all js files in the repo except those under /hash_script/ - verify only whitespace changed: git diff --word-diff-regex=. 62d6a71 62d6a71~1 --- main_background.js | 1900 ++++++++++++++++++++++++++-------------------------- 1 file changed, 952 insertions(+), 948 deletions(-) (limited to 'main_background.js') diff --git a/main_background.js b/main_background.js index 82f3453..0d52308 100644 --- a/main_background.js +++ b/main_background.js @@ -23,10 +23,10 @@ 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"); +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"); /** @@ -39,47 +39,47 @@ var DEBUG = false; // debug the JS evaluation var PRINT_DEBUG = false; // Everything else var time = Date.now(); -function dbg_print(a,b){ - if(PRINT_DEBUG == true){ - console.log("Time spent so far: " + (Date.now() - time)/1000 + " seconds"); - if(b === undefined){ - console.log(a); - } else{ - console.log(a,b); - } - } +function dbg_print(a, b) { + if (PRINT_DEBUG == true) { + console.log("Time spent so far: " + (Date.now() - time) / 1000 + " seconds"); + if (b === undefined) { + console.log(a); + } else { + console.log(a, b); + } + } } /* - NONTRIVIAL THINGS: - - Fetch - - XMLhttpRequest - - eval() - - ? - JAVASCRIPT CAN BE FOUND IN: - - Event handlers (onclick, onload, onsubmit, etc.) - - - - - WAYS TO DETERMINE PASS/FAIL: - - "// @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_ + NONTRIVIAL THINGS: + - Fetch + - XMLhttpRequest + - eval() + - ? + JAVASCRIPT CAN BE FOUND IN: + - Event handlers (onclick, onload, onsubmit, etc.) + - + - + WAYS TO DETERMINE PASS/FAIL: + - "// @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_ */ var licenses = require("./licenses.json").licenses; // 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" + //"document", + //"window", + "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; +function get_storage_key(script_name, src_hash) { + return script_name; } /* @@ -94,24 +94,24 @@ function get_storage_key(script_name,src_hash){ * with its code to update accordingly * */ -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); +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 = ""; - for (var i = 0; i < changedItems.length; i++){ - var item = changedItems[i]; - changed_items += item + ","; - } - dbg_print(changed_items); + var changedItems = Object.keys(changes); + var changed_items = ""; + for (var i = 0; i < changedItems.length; i++) { + var item = changedItems[i]; + changed_items += item + ","; + } + dbg_print(changed_items); } @@ -119,26 +119,26 @@ function options_listener(changes, area){ 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"); - } - let template = { - "accepted": [], - "blocked": [], - "blacklisted": [], - "whitelisted": [], - "unknown": [], - }; - template = Object.assign(template, initializer); - 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]; - if (list) { - template.listedSite = ListManager.siteMatch(template.site, list); - } - return template; + if (!(initializer && (initializer.url || initializer.tabId))) { + throw new Error("createReport() needs an URL or a tabId at least"); + } + let template = { + "accepted": [], + "blocked": [], + "blacklisted": [], + "whitelisted": [], + "unknown": [], + }; + template = Object.assign(template, initializer); + 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]; + if (list) { + template.listedSite = ListManager.siteMatch(template.site, list); + } + return template; } /** @@ -147,9 +147,9 @@ async function createReport(initializer) { * at the moment. */ async function openReportInTab(data) { - let popupURL = await browser.browserAction.getPopup({}); - let tab = await browser.tabs.create({url: `${popupURL}#fromTab=${data.tabId}`}); - activityReports[tab.id] = await createReport(data); + let popupURL = await browser.browserAction.getPopup({}); + let tab = await browser.tabs.create({ url: `${popupURL}#fromTab=${data.tabId}` }); + activityReports[tab.id] = await createReport(data); } /** @@ -157,9 +157,9 @@ async function openReportInTab(data) { * Clears local storage (the persistent data) * */ -function debug_delete_local(){ - browser.storage.local.clear(); - dbg_print("Local storage cleared"); +function debug_delete_local() { + browser.storage.local.clear(); + dbg_print("Local storage cleared"); } /** @@ -167,16 +167,16 @@ function debug_delete_local(){ * Prints local storage (the persistent data) as well as the temporary popup object * */ -function debug_print_local(){ - function storage_got(items){ - console.log("%c Local storage: ", 'color: red;'); - for(var i in items){ - console.log("%c "+i+" = "+items[i], 'color: blue;'); - } - } - console.log("%c Variable 'activityReports': ", 'color: red;'); - console.log(activityReports); - browser.storage.local.get(storage_got); +function debug_print_local() { + function storage_got(items) { + console.log("%c Local storage: ", 'color: red;'); + for (var i in items) { + console.log("%c " + i + " = " + items[i], 'color: blue;'); + } + } + console.log("%c Variable 'activityReports': ", 'color: red;'); + console.log(activityReports); + browser.storage.local.get(storage_got); } /** @@ -194,25 +194,25 @@ function debug_print_local(){ * Make sure it will use the right URL when refering to a certain script. * */ -async function updateReport(tabId, oldReport, updateUI = false){ - let {url} = oldReport; - let newReport = await createReport({url, tabId}); - for (let property of Object.keys(oldReport)) { - let entries = oldReport[property]; - if (!Array.isArray(entries)) continue; - 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); - } - } - activityReports[tabId] = newReport; - if (browser.sessions) browser.sessions.setTabValue(tabId, url, newReport); - dbg_print(newReport); - if (updateUI && activeMessagePorts[tabId]) { - dbg_print(`[TABID: ${tabId}] Sending script blocking report directly to browser action.`); - activeMessagePorts[tabId].postMessage({show_info: newReport}); - } +async function updateReport(tabId, oldReport, updateUI = false) { + let { url } = oldReport; + let newReport = await createReport({ url, tabId }); + for (let property of Object.keys(oldReport)) { + let entries = oldReport[property]; + if (!Array.isArray(entries)) continue; + 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); + } + } + activityReports[tabId] = newReport; + if (browser.sessions) browser.sessions.setTabValue(tabId, url, newReport); + dbg_print(newReport); + if (updateUI && activeMessagePorts[tabId]) { + dbg_print(`[TABID: ${tabId}] Sending script blocking report directly to browser action.`); + activeMessagePorts[tabId].postMessage({ show_info: newReport }); + } } /** @@ -236,69 +236,69 @@ async function updateReport(tabId, oldReport, updateUI = false){ * */ async function addReportEntry(tabId, scriptHashOrUrl, action) { - let report = activityReports[tabId]; - if (!report) report = activityReports[tabId] = - await createReport({tabId}); - let type, actionValue; - 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 ""; - } - - // Search unused data for the given entry - function isNew(entries, item) { - for (let e of entries) { - if (e[0] === item) return false; - } - return true; - } - - let entryType; - let scriptName = actionValue[0]; - try { - entryType = listManager.getStatus(scriptName, type); - let entries = report[entryType]; - if(isNew(entries, scriptName)){ - dbg_print(activityReports); - dbg_print(activityReports[tabId]); - dbg_print(entryType); - entries.push(actionValue); - } - } catch (e) { - 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) { - } - } - - if (browser.sessions) browser.sessions.setTabValue(tabId, report.url, report); - updateBadge(tabId, report); - return entryType; + let report = activityReports[tabId]; + if (!report) report = activityReports[tabId] = + await createReport({ tabId }); + let type, actionValue; + 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 ""; + } + + // Search unused data for the given entry + function isNew(entries, item) { + for (let e of entries) { + if (e[0] === item) return false; + } + return true; + } + + let entryType; + let scriptName = actionValue[0]; + try { + entryType = listManager.getStatus(scriptName, type); + let entries = report[entryType]; + if (isNew(entries, scriptName)) { + dbg_print(activityReports); + dbg_print(activityReports[tabId]); + dbg_print(entryType); + entries.push(actionValue); + } + } catch (e) { + 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) { + } + } + + if (browser.sessions) browser.sessions.setTabValue(tabId, report.url, report); + updateBadge(tabId, report); + return entryType; } -function get_domain(url){ - var domain = url.replace('http://','').replace('https://','').split(/[/?#]/)[0]; - if(url.indexOf("http://") == 0){ - domain = "http://" + domain; - } - else if(url.indexOf("https://") == 0){ - domain = "https://" + domain; - } - domain = domain + "/"; - domain = domain.replace(/ /g,""); - return domain; +function get_domain(url) { + var domain = url.replace('http://', '').replace('https://', '').split(/[/?#]/)[0]; + if (url.indexOf("http://") == 0) { + domain = "http://" + domain; + } + else if (url.indexOf("https://") == 0) { + domain = "https://" + domain; + } + domain = domain + "/"; + domain = domain.replace(/ /g, ""); + return domain; } /** @@ -308,82 +308,82 @@ function get_domain(url){ */ var portFromCS; async function connected(p) { - if(p.name === "contact_finder"){ - // style the contact finder panel - await browser.tabs.insertCSS(p.sender.tab.id, { - 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"])); - return; - } - p.onMessage.addListener(async function(m) { - var update = false; - var contact_finder = false; - - for (let action of ["whitelist", "blacklist", "forget"]) { - if (m[action]) { - let [key] = m[action]; - if (m.site) { - key = ListStore.siteItem(m.site); - } else { - key = ListStore.inlineItem(key) || key; - } - await listManager[action](key); - update = true; - } - } - - if(m.report_tab){ - openReportInTab(m.report_tab); - } - // a debug feature - if(m["printlocalstorage"] !== undefined){ - console.log("Print local storage"); - debug_print_local(); - } - // invoke_contact_finder - 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"); - debug_delete_local(); - } - - 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 || m.update && activityReports[m.tabId]) { - 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 { - for(let tab of tabs) { - 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]}); - } else{ - // create a new entry - 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.`); - } - } - } - }); + if (p.name === "contact_finder") { + // style the contact finder panel + await browser.tabs.insertCSS(p.sender.tab.id, { + 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"])); + return; + } + p.onMessage.addListener(async function(m) { + var update = false; + var contact_finder = false; + + for (let action of ["whitelist", "blacklist", "forget"]) { + if (m[action]) { + let [key] = m[action]; + if (m.site) { + key = ListStore.siteItem(m.site); + } else { + key = ListStore.inlineItem(key) || key; + } + await listManager[action](key); + update = true; + } + } + + if (m.report_tab) { + openReportInTab(m.report_tab); + } + // a debug feature + if (m["printlocalstorage"] !== undefined) { + console.log("Print local storage"); + debug_print_local(); + } + // invoke_contact_finder + 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"); + debug_delete_local(); + } + + 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 || m.update && activityReports[m.tabId]) { + 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 { + for (let tab of tabs) { + 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] }); + } else { + // create a new entry + 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,15 +392,15 @@ 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"); - if(activityReports[tab_id] !== undefined){ - delete activityReports[tab_id]; - } - if(activeMessagePorts[tab_id] !== undefined){ - delete activeMessagePorts[tab_id]; - } - ExternalLicenses.purgeCache(tab_id); +function delete_removed_tab_info(tab_id, remove_info) { + dbg_print("[TABID:" + tab_id + "]" + "Deleting stored info about closed tab"); + if (activityReports[tab_id] !== undefined) { + delete activityReports[tab_id]; + } + if (activeMessagePorts[tab_id] !== undefined) { + delete activeMessagePorts[tab_id]; + } + ExternalLicenses.purgeCache(tab_id); } /** @@ -412,19 +412,19 @@ function delete_removed_tab_info(tab_id, remove_info){ */ async function onTabUpdated(tabId, changedInfo, tab) { - let [url] = tab.url.split("#"); - let report = activityReports[tabId]; - if (!(report && report.url === url)) { - let cache = browser.sessions && - await browser.sessions.getTabValue(tabId, url) || null; - // on session restore tabIds may change - if (cache && cache.tabId !== tabId) cache.tabId = tabId; - updateBadge(tabId, activityReports[tabId] = cache); - } + let [url] = tab.url.split("#"); + let report = activityReports[tabId]; + if (!(report && report.url === url)) { + let cache = browser.sessions && + await browser.sessions.getTabValue(tabId, url) || null; + // on session restore tabIds may change + if (cache && cache.tabId !== tabId) cache.tabId = tabId; + updateBadge(tabId, activityReports[tabId] = cache); + } } -async function onTabActivated({tabId}) { - await onTabUpdated(tabId, {}, await browser.tabs.get(tabId)); +async function onTabActivated({ tabId }) { + await onTabUpdated(tabId, {}, await browser.tabs.get(tabId)); } /* *********************************************************************************************** */ @@ -433,128 +433,128 @@ 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"]; - } - - 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}; - 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)"]; - } - 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"); - } - - /** - * 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){ - 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) == "["; - } - var error_count = 0; - var defines_functions = false; - while(toke !== undefined && toke.type != acorn.tokTypes.eof){ - if(toke.type.keyword !== undefined){ - //dbg_print("Keyword:"); - //dbg_print(toke); - - // 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"); - defines_functions = true; - } - - if(loopkeys[toke.type.keyword] !== undefined){ - amtloops++; - if(amtloops > 3){ - dbg_print("%c NONTRIVIAL: Too many loops/conditionals.","color:red"); - if(DEBUG == false){ - return [false,"NONTRIVIAL: Too many loops/conditionals."]; - } - } - } - }else if(toke.value !== undefined && operators[toke.value] !== undefined){ - // It's just an operator. Javascript doesn't have operator overloading so it must be some - // kind of primitive (I.e. a number) - }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"); - if(DEBUG == false){ - 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"); - if(DEBUG == false){ - 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"); - if(DEBUG == false){ - return [false,"NONTRIVIAL: Bracket suffix notation on variable '"+toke.value+"'"]; - } - } - }else{ - 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("%cAppears to be trivial.","color:green;"); - if (defines_functions === true) - return [true,"Script appears to be trivial but defines functions."]; - else - return [true,"Script appears to be trivial."]; +function full_evaluate(script) { + var res = true; + 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 }; + 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)"]; + } + 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"); + } + + /** + * 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) { + 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) == "["; + } + var error_count = 0; + var defines_functions = false; + while (toke !== undefined && toke.type != acorn.tokTypes.eof) { + if (toke.type.keyword !== undefined) { + //dbg_print("Keyword:"); + //dbg_print(toke); + + // 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"); + defines_functions = true; + } + + if (loopkeys[toke.type.keyword] !== undefined) { + amtloops++; + if (amtloops > 3) { + dbg_print("%c NONTRIVIAL: Too many loops/conditionals.", "color:red"); + if (DEBUG == false) { + return [false, "NONTRIVIAL: Too many loops/conditionals."]; + } + } + } + } else if (toke.value !== undefined && operators[toke.value] !== undefined) { + // It's just an operator. Javascript doesn't have operator overloading so it must be some + // kind of primitive (I.e. a number) + } 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"); + if (DEBUG == false) { + 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"); + if (DEBUG == false) { + 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"); + if (DEBUG == false) { + return [false, "NONTRIVIAL: Bracket suffix notation on variable '" + toke.value + "'"]; + } + } + } else { + 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("%cAppears to be trivial.", "color:green;"); + if (defines_functions === true) + return [true, "Script appears to be trivial but defines functions."]; + else + return [true, "Script appears to be trivial."]; } @@ -571,66 +571,66 @@ function full_evaluate(script){ * It returns an array of [flag (boolean, false if "bad"), reason (string, human readable report)] * */ -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"); - } - reserved_object_regex("window"); - var all_strings = new RegExp('".*?"'+"|'.*?'","gm"); - var ml_comment = /\/\*([\s\S]+?)\*\//g; - var il_comment = /\/\/.+/gm; - var bracket_pairs = /\[.+?\]/g; - 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?"); - var flag = true; - 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;"); - flag = false; - reason = "Script uses a reserved object (" + reserved_objects[i] + ")"; - } - } - if(flag){ - dbg_print("%c pass","color:green;"); - } else{ - return [flag,reason]; - } - - return 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"); + } + reserved_object_regex("window"); + var all_strings = new RegExp('".*?"' + "|'.*?'", "gm"); + var ml_comment = /\/\*([\s\S]+?)\*\//g; + var il_comment = /\/\/.+/gm; + var bracket_pairs = /\[.+?\]/g; + 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?"); + var flag = true; + 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;"); + flag = false; + reason = "Script uses a reserved object (" + reserved_objects[i] + ")"; + } + } + if (flag) { + dbg_print("%c pass", "color:green;"); + } else { + return [flag, reason]; + } + + return full_evaluate(script); } function validateLicense(matches) { - if (!(Array.isArray(matches) && matches.length >= 4)){ - return [false, "Malformed or unrecognized license tag."]; - } - - let [all, tag, first, second] = matches; - - for (let key in licenses){ - // Match by id on first or second parameter, ignoring case - if (key.toLowerCase() === first.toLowerCase() || - key.toLowerCase() === second.toLowerCase()) { - return [true, `Recognized license: "${licenses[key]['Name']}" `]; - } - // Match by link on first parameter (legacy) - if (licenses[key]["Magnet link"] === first.replace("&","&") || - licenses[key]["URL"] === first.replace("&","&") || - licenses[key]["URL"].replace("http://", "https://") === first.replace("&","&")) { - return [true, `Recognized license: "${licenses[key]['Name']}".`]; - } - } - return [false, `Unrecognized license tag: "${all}"`]; + if (!(Array.isArray(matches) && matches.length >= 4)) { + return [false, "Malformed or unrecognized license tag."]; + } + + let [all, tag, first, second] = matches; + + for (let key in licenses) { + // Match by id on first or second parameter, ignoring case + if (key.toLowerCase() === first.toLowerCase() || + key.toLowerCase() === second.toLowerCase()) { + return [true, `Recognized license: "${licenses[key]['Name']}" `]; + } + // Match by link on first parameter (legacy) + if (licenses[key]["Magnet link"] === first.replace("&", "&") || + licenses[key]["URL"] === first.replace("&", "&") || + licenses[key]["URL"].replace("http://", "https://") === first.replace("&", "&")) { + return [true, `Recognized license: "${licenses[key]['Name']}".`]; + } + } + return [false, `Unrecognized license tag: "${all}"`]; } @@ -645,92 +645,92 @@ function validateLicense(matches) { * reason text * ] */ -function license_read(scriptSrc, name, external = false){ - - let license = legacy_license_lib.check(scriptSrc); - if (license){ - return [true, scriptSrc, `Licensed under: ${license}`]; - } - if (listManager.builtInHashes.has(hash(scriptSrc))){ - return [true, scriptSrc, "Common script known to be free software."]; - } - - let editedSrc = ""; - let uneditedSrc = scriptSrc.trim(); - let reason = uneditedSrc ? "" : "Empty source."; - let partsDenied = false; - let partsAccepted = false; - - function checkTriviality(s) { - if (!s.trim()) { - return true; // empty, ignore it - } - let [trivial, message] = external ? - [false, "External script with no known license"] - : evaluate(s, name); - if (trivial) { - partsAccepted = true; - editedSrc += s; - } else { - partsDenied = true; - if (s.startsWith("javascript:")) - editedSrc += `# LIBREJS BLOCKED: ${message}`; - else - editedSrc += `/*\nLIBREJS BLOCKED: ${message}\n*/`; - } - reason += `\n${message}`; - return trivial; - } - - while (uneditedSrc) { - let openingMatch = /\/[\/\*]\s*?(@license)\s+(\S+)\s+(\S+)\s*$/mi.exec(uneditedSrc); - if (!openingMatch) { // no license found, check for triviality - checkTriviality(uneditedSrc); - break; - } - - let openingIndex = openingMatch.index; - if (openingIndex) { - // let's check the triviality of the code before the license tag, if any - checkTriviality(uneditedSrc.substring(0, openingIndex)); - } - // let's check the actual license - uneditedSrc = uneditedSrc.substring(openingIndex); - - let closureMatch = /\/([*/])\s*@license-end\b[^*/\n]*/i.exec(uneditedSrc); - if (!closureMatch) { - let msg = "ERROR: @license with no @license-end"; - return [false, `\n/*\n ${msg} \n*/\n`, msg]; - } - - let closureEndIndex = closureMatch.index + closureMatch[0].length; - let commentEndOffset = uneditedSrc.substring(closureEndIndex).indexOf(closureMatch[1] === "*" ? "*/" : "\n"); - if (commentEndOffset !== -1) { - closureEndIndex += commentEndOffset; - } - - let [licenseOK, message] = validateLicense(openingMatch); - if(licenseOK) { - editedSrc += uneditedSrc.substr(0, closureEndIndex); - partsAccepted = true; - } else { - editedSrc += `\n/*\n${message}\n*/\n`; - partsDenied = true; - } - reason += `\n${message}`; - - // trim off everything we just evaluated - uneditedSrc = uneditedSrc.substring(closureEndIndex).trim(); - } - - if(partsDenied) { - if (partsAccepted) { - reason = `Some parts of the script have been disabled (check the source for details).\n^--- ${reason}`; - } - return [false, editedSrc, reason]; - } - - return [true, scriptSrc, reason]; +function license_read(scriptSrc, name, external = false) { + + let license = legacy_license_lib.check(scriptSrc); + if (license) { + return [true, scriptSrc, `Licensed under: ${license}`]; + } + if (listManager.builtInHashes.has(hash(scriptSrc))) { + return [true, scriptSrc, "Common script known to be free software."]; + } + + let editedSrc = ""; + let uneditedSrc = scriptSrc.trim(); + let reason = uneditedSrc ? "" : "Empty source."; + let partsDenied = false; + let partsAccepted = false; + + function checkTriviality(s) { + if (!s.trim()) { + return true; // empty, ignore it + } + let [trivial, message] = external ? + [false, "External script with no known license"] + : evaluate(s, name); + if (trivial) { + partsAccepted = true; + editedSrc += s; + } else { + partsDenied = true; + if (s.startsWith("javascript:")) + editedSrc += `# LIBREJS BLOCKED: ${message}`; + else + editedSrc += `/*\nLIBREJS BLOCKED: ${message}\n*/`; + } + reason += `\n${message}`; + return trivial; + } + + while (uneditedSrc) { + let openingMatch = /\/[\/\*]\s*?(@license)\s+(\S+)\s+(\S+)\s*$/mi.exec(uneditedSrc); + if (!openingMatch) { // no license found, check for triviality + checkTriviality(uneditedSrc); + break; + } + + let openingIndex = openingMatch.index; + if (openingIndex) { + // let's check the triviality of the code before the license tag, if any + checkTriviality(uneditedSrc.substring(0, openingIndex)); + } + // let's check the actual license + uneditedSrc = uneditedSrc.substring(openingIndex); + + let closureMatch = /\/([*/])\s*@license-end\b[^*/\n]*/i.exec(uneditedSrc); + if (!closureMatch) { + let msg = "ERROR: @license with no @license-end"; + return [false, `\n/*\n ${msg} \n*/\n`, msg]; + } + + let closureEndIndex = closureMatch.index + closureMatch[0].length; + let commentEndOffset = uneditedSrc.substring(closureEndIndex).indexOf(closureMatch[1] === "*" ? "*/" : "\n"); + if (commentEndOffset !== -1) { + closureEndIndex += commentEndOffset; + } + + let [licenseOK, message] = validateLicense(openingMatch); + if (licenseOK) { + editedSrc += uneditedSrc.substr(0, closureEndIndex); + partsAccepted = true; + } else { + editedSrc += `\n/*\n${message}\n*/\n`; + partsDenied = true; + } + reason += `\n${message}`; + + // trim off everything we just evaluated + uneditedSrc = uneditedSrc.substring(closureEndIndex).trim(); + } + + if (partsDenied) { + if (partsAccepted) { + reason = `Some parts of the script have been disabled (check the source for details).\n^--- ${reason}`; + } + return [false, editedSrc, reason]; + } + + return [true, scriptSrc, reason]; } /* *********************************************************************************************** */ @@ -741,90 +741,92 @@ function license_read(scriptSrc, name, external = false){ * or an array containing it and the index, if the latter !== -1 */ async function get_script(response, url, tabId = -1, whitelisted = false, index = -1) { - function result(scriptSource) { - return index === -1 ? scriptSource : [scriptSource, index]; - } - - - 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}); - } - if (response.startsWith("javascript:")) - return result(response); - else - return result(`/* LibreJS: script whitelisted by user preference. */\n${response}`); - } - - let [verdict, editedSource, reason] = license_read(response, scriptName, index === -2); - - if (tabId < 0) { - 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]}); - switch(category) { - case "blacklisted": - editedSource = `/* LibreJS: script ${category} by user. */`; - return result(response.startsWith("javascript:") - ? `javascript:void(${encodeURIComponent(editedSource)})` : editedSource); - case "whitelisted": - return result(response.startsWith("javascript:") - ? response : `/* LibreJS: script ${category} by user. */\n${response}`); - default: - let scriptSource = verdict ? response : editedSource; + function result(scriptSource) { + return index === -1 ? scriptSource : [scriptSource, index]; + } + + + 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 }); + } + if (response.startsWith("javascript:")) + return result(response); + else + return result(`/* LibreJS: script whitelisted by user preference. */\n${response}`); + } + + let [verdict, editedSource, reason] = license_read(response, scriptName, index === -2); + + if (tabId < 0) { + 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] }); + switch (category) { + case "blacklisted": + editedSource = `/* LibreJS: script ${category} by user. */`; + return result(response.startsWith("javascript:") + ? `javascript:void(${encodeURIComponent(editedSource)})` : editedSource); + case "whitelisted": + return result(response.startsWith("javascript:") + ? response : `/* LibreJS: script ${category} by user. */\n${response}`); + default: + let scriptSource = verdict ? response : editedSource; return result(response.startsWith("javascript:") - ? (verdict ? scriptSource : `javascript:void(/* ${scriptSource} */)`) - : `/* LibreJS: script ${category}. */\n${scriptSource}` - ); - } + ? (verdict ? scriptSource : `javascript:void(/* ${scriptSource} */)`) + : `/* LibreJS: script ${category}. */\n${scriptSource}` + ); + } } 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"] - let {browserAction} = browser; - if ("setBadgeText" in browserAction) { - browserAction.setBadgeText({text, tabId}); - browserAction.setBadgeBackgroundColor({color, tabId}); - } else { - // Mobile - browserAction.setTitle({title: `LibreJS (${text})`, tabId}); - } + let blockedCount = report ? report.blocked.length + report.blacklisted.length : 0; + let [text, color] = blockedCount > 0 || forceRed + ? [blockedCount && blockedCount.toString() || "!", "red"] : ["✓", "green"] + let { browserAction } = browser; + if ("setBadgeText" in browserAction) { + browserAction.setBadgeText({ text, tabId }); + browserAction.setBadgeBackgroundColor({ color, tabId }); + } else { + // Mobile + browserAction.setTitle({ title: `LibreJS (${text})`, tabId }); + } } function blockGoogleAnalytics(request) { - let {url} = request; - let res = {}; - if (url === 'https://www.google-analytics.com/analytics.js' || - /^https:\/\/www\.google\.com\/analytics\/[^#]/.test(url) - ) { - res.cancel = true; - } - return res; + let { url } = request; + let res = {}; + if (url === 'https://www.google-analytics.com/analytics.js' || + /^https:\/\/www\.google\.com\/analytics\/[^#]/.test(url) + ) { + res.cancel = true; + } + return res; } -async function blockBlacklistedScripts(request) { - let {url, tabId, documentUrl} = request; - url = ListStore.urlItem(url); - let status = listManager.getStatus(url); - if (status !== "blacklisted") return {}; - let blacklistedSite = ListManager.siteMatch(url, blacklist); - await addReportEntry(tabId, url, {url: documentUrl, - "blacklisted": [url, /\*/.test(blacklistedSite) ? `User blacklisted ${blacklistedSite}` : "Blacklisted by user"]}); - return {cancel: true}; +async function blockBlacklistedScripts(request) { + let { url, tabId, documentUrl } = request; + url = ListStore.urlItem(url); + let status = listManager.getStatus(url); + if (status !== "blacklisted") return {}; + let blacklistedSite = ListManager.siteMatch(url, blacklist); + await addReportEntry(tabId, url, { + url: documentUrl, + "blacklisted": [url, /\*/.test(blacklistedSite) ? `User blacklisted ${blacklistedSite}` : "Blacklisted by user"] + }); + return { cancel: true }; } /** @@ -834,91 +836,93 @@ async function blockBlacklistedScripts(request) { */ var ResponseHandler = { - /** - * Enforce white/black lists for url/site early (hashes will be handled later) - */ - async pre(response) { - let {request} = response; - let {url, type, tabId, frameId, documentUrl} = request; - - let fullUrl = url; - url = ListStore.urlItem(url); - let site = ListStore.siteItem(url); - - let blacklistedSite = ListManager.siteMatch(site, blacklist); - let blacklisted = blacklistedSite || blacklist.contains(url); - let topUrl = type === "sub_frame" && request.frameAncestors && request.frameAncestors.pop() || documentUrl; - - if (blacklisted) { - 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() - 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}); - } - // use CSP to restrict JavaScript execution in the page - request.responseHeaders.unshift({ - 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 (whitelisted) { - // accept the script and stop processing - addReportEntry(tabId, url, {url: topUrl, - "whitelisted": [url, whitelistedSite ? `User whitelisted ${whitelistedSite}` : "Whitelisted by user"]}); - return ResponseProcessor.ACCEPT; - } else { - let scriptInfo = await ExternalLicenses.check({url: fullUrl, tabId, frameId, documentUrl}); - if (scriptInfo) { - let verdict, ret; - let msg = scriptInfo.toString(); - if (scriptInfo.free) { - verdict = "accepted"; - ret = ResponseProcessor.ACCEPT; - } else { - verdict = "blocked"; - ret = ResponseProcessor.REJECT; - } - addReportEntry(tabId, url, {url, [verdict]: [url, msg]}); - return ret; - } - } - } - } - // it's a page (it's too early to report) or an unknown script: - // let's keep processing - return ResponseProcessor.CONTINUE; - }, - - /** - * Here we do the heavylifting, analyzing unknown scripts - */ - async post(response) { - let {type} = response.request; - let handle_it = type === "script" ? handle_script : handle_html; - return await handle_it(response, response.whitelisted); - } + /** + * Enforce white/black lists for url/site early (hashes will be handled later) + */ + async pre(response) { + let { request } = response; + let { url, type, tabId, frameId, documentUrl } = request; + + let fullUrl = url; + url = ListStore.urlItem(url); + let site = ListStore.siteItem(url); + + let blacklistedSite = ListManager.siteMatch(site, blacklist); + let blacklisted = blacklistedSite || blacklist.contains(url); + let topUrl = type === "sub_frame" && request.frameAncestors && request.frameAncestors.pop() || documentUrl; + + if (blacklisted) { + 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() + 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 }); + } + // use CSP to restrict JavaScript execution in the page + request.responseHeaders.unshift({ + 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 (whitelisted) { + // accept the script and stop processing + addReportEntry(tabId, url, { + url: topUrl, + "whitelisted": [url, whitelistedSite ? `User whitelisted ${whitelistedSite}` : "Whitelisted by user"] + }); + return ResponseProcessor.ACCEPT; + } else { + let scriptInfo = await ExternalLicenses.check({ url: fullUrl, tabId, frameId, documentUrl }); + if (scriptInfo) { + let verdict, ret; + let msg = scriptInfo.toString(); + if (scriptInfo.free) { + verdict = "accepted"; + ret = ResponseProcessor.ACCEPT; + } else { + verdict = "blocked"; + ret = ResponseProcessor.REJECT; + } + addReportEntry(tabId, url, { url, [verdict]: [url, msg] }); + return ret; + } + } + } + } + // it's a page (it's too early to report) or an unknown script: + // let's keep processing + return ResponseProcessor.CONTINUE; + }, + + /** + * Here we do the heavylifting, analyzing unknown scripts + */ + async post(response) { + let { type } = response.request; + let handle_it = type === "script" ? handle_script : handle_html; + return await handle_it(response, response.whitelisted); + } } /** * Here we handle external script requests */ -async function handle_script(response, whitelisted){ - let {text, request} = response; - let {url, tabId, frameId} = request; - url = ListStore.urlItem(url); +async function handle_script(response, whitelisted) { + let { text, request } = response; + let { url, tabId, frameId } = request; + url = ListStore.urlItem(url); let edited = await get_script(text, url, tabId, whitelisted, -2); - return Array.isArray(edited) ? edited[0] : edited; + return Array.isArray(edited) ? edited[0] : edited; } /** @@ -926,15 +930,15 @@ async function handle_script(response, whitelisted){ * the DOCTYPE declaration */ function doc2HTML(doc) { - let s = doc.documentElement.outerHTML; - if (doc.doctype) { - let dt = doc.doctype; - let sDoctype = `\n${s}`; - } - return s; + let s = doc.documentElement.outerHTML; + if (doc.doctype) { + let dt = doc.doctype; + let sDoctype = `\n${s}`; + } + return s; } /** @@ -949,10 +953,10 @@ 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"); - replacement.innerHTML = element.innerHTML; - element.replaceWith(replacement); - return replacement; + let replacement = createHTMLElement(doc, "span"); + replacement.innerHTML = element.innerHTML; + element.replaceWith(replacement); + return replacement; } /** @@ -961,9 +965,9 @@ function forceElement(doc, element) { * they have the "data-librejs-nodisplay" attribute). */ function forceNoscriptElements(doc) { - let shown = 0; - // inspired by NoScript's onScriptDisabled.js - for (let noscript of doc.querySelectorAll("noscript:not([data-librejs-nodisplay])")) { + let shown = 0; + // inspired by NoScript's onScriptDisabled.js + 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"]'); @@ -971,9 +975,9 @@ function forceNoscriptElements(doc) { refresh = true; doc.head.appendChild(meta); } - shown++; + shown++; } - return shown; + return shown; } /** * Forces displaying any element having the "data-librejs-display" attribute and @@ -981,232 +985,232 @@ function forceNoscriptElements(doc) { * they have the "data-librejs-nodisplay" attribute). */ function showConditionalElements(doc) { - let shown = 0; - for (let element of document.querySelectorAll("[data-librejs-display]")) { - forceElement(doc, element); - shown++; - } - return shown; + let shown = 0; + for (let element of document.querySelectorAll("[data-librejs-display]")) { + forceElement(doc, element); + shown++; + } + return shown; } /** * Tests to see if the intrinsic events on the page are free or not. * returns true if they are, false if they're not */ -function read_metadata(meta_element){ - - if(meta_element === undefined || meta_element === null){ - return; - } - - console.log("metadata found"); - - var metadata = {}; - - try{ - metadata = JSON.parse(meta_element.innerHTML); - }catch(error){ - console.log("Could not parse metadata on page.") - return false; - } - - var license_str = metadata["intrinsic-events"]; - if(license_str === undefined){ - console.log("No intrinsic events license"); - return false; - } - console.log(license_str); - - var parts = license_str.split(" "); - if(parts.length != 2){ - console.log("invalid (>2 tokens)"); - return false; - } - - // this should be adequete to escape the HTML escaping - parts[0] = parts[0].replace(/&/g, '&'); - - try{ - if(licenses[parts[1]]["Magnet link"] == parts[0]){ - return true; - }else{ - console.log("invalid (doesn't match licenses)"); - return false; - } - } catch(error){ - console.log("invalid (threw error, key didn't exist)"); - return false; - } +function read_metadata(meta_element) { + + if (meta_element === undefined || meta_element === null) { + return; + } + + console.log("metadata found"); + + var metadata = {}; + + try { + metadata = JSON.parse(meta_element.innerHTML); + } catch (error) { + console.log("Could not parse metadata on page.") + return false; + } + + var license_str = metadata["intrinsic-events"]; + if (license_str === undefined) { + console.log("No intrinsic events license"); + return false; + } + console.log(license_str); + + var parts = license_str.split(" "); + if (parts.length != 2) { + console.log("invalid (>2 tokens)"); + return false; + } + + // this should be adequete to escape the HTML escaping + parts[0] = parts[0].replace(/&/g, '&'); + + try { + if (licenses[parts[1]]["Magnet link"] == parts[0]) { + return true; + } else { + console.log("invalid (doesn't match licenses)"); + return false; + } + } catch (error) { + console.log("invalid (threw error, key didn't exist)"); + return false; + } } /** * Reads/changes the HTML of a page and the scripts within it. */ -async function editHtml(html, documentUrl, tabId, frameId, whitelisted){ - - var parser = new DOMParser(); - var html_doc = parser.parseFromString(html, "text/html"); - - // moves external licenses reference, if any, before any