aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.js17
-rw-r--r--bg/ExternalLicenses.js29
-rw-r--r--bg/ListManager.js20
-rw-r--r--bg/ResponseMetaData.js18
-rw-r--r--bg/ResponseProcessor.js32
-rw-r--r--common/Storage.js24
-rw-r--r--common/Test.js6
-rw-r--r--main_background.js427
-rw-r--r--test/spec/LibreJSSpec.js140
9 files changed, 344 insertions, 369 deletions
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..d48f883
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,17 @@
+module.exports = {
+ "env": {
+ "browser": true,
+ "commonjs": true,
+ "es2021": true,
+ "node": true,
+ "webextensions": true
+ },
+ "extends": "eslint:recommended",
+ "parserOptions": {
+ "ecmaVersion": "latest"
+ },
+ "rules": {
+ "quotes": ["error", "single"],
+ "no-console": ["off"]
+ }
+}
diff --git a/bg/ExternalLicenses.js b/bg/ExternalLicenses.js
index 0e09b6d..1a8e58c 100644
--- a/bg/ExternalLicenses.js
+++ b/bg/ExternalLicenses.js
@@ -23,12 +23,12 @@
Singleton to handle external licenses, e.g. WebLabels
*/
-"use strict";
+'use strict';
let licensesByLabel = new Map();
let licensesByUrl = new Map();
{
- let { licenses } = require("../license_definitions");
+ let { licenses } = require('../license_definitions');
let mapByLabel = (label, license) => licensesByLabel.set(label.toUpperCase(), license);
for (let [id, l] of Object.entries(licenses)) {
let { identifier, canonicalUrl, licenseName } = l;
@@ -64,7 +64,7 @@ var ExternalLicenses = {
let frameCache = tabCache && tabCache.get(frameId);
let cache = frameCache && frameCache.get(documentUrl);
let scriptInfo = await browser.tabs.sendMessage(tabId, {
- action: "checkLicensedScript",
+ action: 'checkLicensedScript',
url,
cache,
}, { frameId });
@@ -74,10 +74,10 @@ var ExternalLicenses = {
}
scriptInfo.licenses = new Set();
scriptInfo.toString = function() {
- let licenseIds = [...this.licenses].map(l => l.identifier).sort().join(", ");
+ let licenseIds = [...this.licenses].map(l => l.identifier).sort().join(', ');
return licenseIds
- ? `Free license${this.licenses.size > 1 ? "s" : ""} (${licenseIds})`
- : "Unknown license(s)";
+ ? `Free license${this.licenses.size > 1 ? 's' : ''} (${licenseIds})`
+ : 'Unknown license(s)';
}
let match = (map, key) => {
if (map.has(key)) {
@@ -112,22 +112,23 @@ var ExternalLicenses = {
}
frameCache.set(frameId, new Map([[documentUrl, cache]]));
- let link = doc.querySelector(`link[rel="jslicense"], link[data-jslicense="1"], a[rel="jslicense"], a[data-jslicense="1"]`);
+ let link = doc.querySelector('link[rel="jslicense"], link[data-jslicense="1"], a[rel="jslicense"], a[data-jslicense="1"]');
if (link) {
- let href = link.getAttribute("href");
+ let href = link.getAttribute('href');
cache.webLabels = { href };
let move = () => !!doc.head.insertBefore(link, doc.head.firstChild);
if (link.parentNode === doc.head) {
- for (let node = link; node = node.previousElementSibling;) {
- if (node.tagName.toUpperCase() === "SCRIPT") {
+ let node = link.previousElementSibling;
+ for (; node; node = node.previousElementSibling) {
+ if (node.tagName.toUpperCase() === 'SCRIPT') {
return move();
}
}
} else { // the reference is only in the body
- if (link.tagName.toUpperCase() === "A") {
- let newLink = doc.createElement("link");
- newLink.rel = "jslicense";
- newLink.setAttribute("href", href);
+ if (link.tagName.toUpperCase() === 'A') {
+ let newLink = doc.createElement('link');
+ newLink.rel = 'jslicense';
+ newLink.setAttribute('href', href);
link = newLink;
}
return move();
diff --git a/bg/ListManager.js b/bg/ListManager.js
index f712356..3020197 100644
--- a/bg/ListManager.js
+++ b/bg/ListManager.js
@@ -23,7 +23,7 @@
A class to manage whitelist/blacklist operations
*/
-let { ListStore } = require("../common/Storage");
+let { ListStore } = require('../common/Storage');
class ListManager {
constructor(whitelist, blacklist, builtInHashes) {
@@ -48,13 +48,13 @@ class ListManager {
with a trailing (hash).
Returns "blacklisted", "whitelisted" or defValue
*/
- getStatus(key, defValue = "unknown") {
+ getStatus(key, defValue = 'unknown') {
let { blacklist, whitelist } = this.lists;
let inline = ListStore.inlineItem(key);
if (inline) {
return blacklist.contains(inline)
- ? "blacklisted"
- : whitelist.contains(inline) ? "whitelisted"
+ ? 'blacklisted'
+ : whitelist.contains(inline) ? 'whitelisted'
: defValue;
}
@@ -63,16 +63,16 @@ class ListManager {
let url = ListStore.urlItem(key);
let site = ListStore.siteItem(key);
return (blacklist.contains(url) || ListManager.siteMatch(site, blacklist)
- ? "blacklisted"
+ ? 'blacklisted'
: whitelist.contains(url) || ListManager.siteMatch(site, whitelist)
- ? "whitelisted" : defValue
+ ? 'whitelisted' : defValue
);
}
let [hashItem, srcHash] = match; // (hash), hash
- return blacklist.contains(hashItem) ? "blacklisted"
+ return blacklist.contains(hashItem) ? 'blacklisted'
: this.builtInHashes.has(srcHash) || whitelist.contains(hashItem)
- ? "whitelisted"
+ ? 'whitelisted'
: defValue;
}
@@ -85,13 +85,13 @@ class ListManager {
if (list.contains(site)) {
return site;
}
- site = site.replace(/^([\w-]+:\/\/)?(\w)/, "$1*.$2");
+ site = site.replace(/^([\w-]+:\/\/)?(\w)/, '$1*.$2');
for (; ;) {
if (list.contains(site)) {
return site;
}
let oldKey = site;
- site = site.replace(/(?:\*\.)*\w+(?=\.)/, "*");
+ site = site.replace(/(?:\*\.)*\w+(?=\.)/, '*');
if (site === oldKey) {
return null;
}
diff --git a/bg/ResponseMetaData.js b/bg/ResponseMetaData.js
index 4570120..cb3fb2a 100644
--- a/bg/ResponseMetaData.js
+++ b/bg/ResponseMetaData.js
@@ -34,24 +34,24 @@ class ResponseMetaData {
this.headers = {};
for (let h of responseHeaders) {
if (/^\s*Content-(Type|Disposition)\s*$/i.test(h.name)) {
- let propertyName = h.name.split("-")[1].trim();
+ let propertyName = h.name.split('-')[1].trim();
propertyName = `content${propertyName.charAt(0).toUpperCase()}${propertyName.substring(1).toLowerCase()}`;
this[propertyName] = h.value;
this.headers[propertyName] = h;
}
}
- this.computedCharset = "";
+ this.computedCharset = '';
}
get charset() {
- let charset = "";
+ let charset = '';
if (this.contentType) {
let m = this.contentType.match(/;\s*charset\s*=\s*(\S+)/);
if (m) {
charset = m[1];
}
}
- Object.defineProperty(this, "charset", { value: charset, writable: false, configurable: true });
+ Object.defineProperty(this, 'charset', { value: charset, writable: false, configurable: true });
return this.computedCharset = charset;
}
@@ -69,12 +69,12 @@ class ResponseMetaData {
// let's try figuring out the charset from <meta> tags
let parser = new DOMParser();
- let doc = parser.parseFromString(text, "text/html");
+ let doc = parser.parseFromString(text, 'text/html');
let meta = doc.querySelectorAll('meta[charset], meta[http-equiv="content-type"], meta[content*="charset"]');
for (let m of meta) {
- charset = m.getAttribute("charset");
+ charset = m.getAttribute('charset');
if (!charset) {
- let match = m.getAttribute("content").match(/;\s*charset\s*=\s*([\w-]+)/i)
+ let match = m.getAttribute('content').match(/;\s*charset\s*=\s*([\w-]+)/i)
if (match) charset = match[1];
}
if (charset) {
@@ -89,7 +89,7 @@ class ResponseMetaData {
return text;
}
- createDecoder(charset = this.charset, def = "latin1") {
+ createDecoder(charset = this.charset, def = 'latin1') {
if (charset) {
try {
return new TextDecoder(charset);
@@ -99,7 +99,7 @@ class ResponseMetaData {
}
return def ? new TextDecoder(def) : null;
}
-};
+}
ResponseMetaData.UTF8BOM = new Uint8Array(BOM);
module.exports = { ResponseMetaData };
diff --git a/bg/ResponseProcessor.js b/bg/ResponseProcessor.js
index 078f38a..d964dab 100644
--- a/bg/ResponseProcessor.js
+++ b/bg/ResponseProcessor.js
@@ -24,22 +24,22 @@
only the "interesting" HTML and script requests and leaving the other alone
*/
-let { ResponseMetaData } = require("./ResponseMetaData");
+let { ResponseMetaData } = require('./ResponseMetaData');
let listeners = new WeakMap();
let webRequestEvent = browser.webRequest.onHeadersReceived;
class ResponseProcessor {
- static install(handler, types = ["main_frame", "sub_frame", "script"]) {
+ static install(handler, types = ['main_frame', 'sub_frame', 'script']) {
if (listeners.has(handler)) return false;
let listener =
async request => await new ResponseTextFilter(request).process(handler);
listeners.set(handler, listener);
webRequestEvent.addListener(
listener,
- { urls: ["<all_urls>"], types },
- ["blocking", "responseHeaders"]
+ { urls: ['<all_urls>'], types },
+ ['blocking', 'responseHeaders']
);
return true;
}
@@ -67,21 +67,21 @@ class ResponseTextFilter {
this.canProcess = // we want to process html documents and scripts only
(statusCode < 300 || statusCode >= 400) && // skip redirections
!md.disposition && // skip forced downloads
- (type === "script" || /\bhtml\b/i.test(md.contentType));
+ (type === 'script' || /\bhtml\b/i.test(md.contentType));
}
async process(handler) {
if (!this.canProcess) return ResponseProcessor.ACCEPT;
let { metaData, request } = this;
let response = { request, metaData }; // we keep it around allowing callbacks to store state
- if (typeof handler.pre === "function") {
+ if (typeof handler.pre === 'function') {
let res = await handler.pre(response);
if (res) return res;
if (handler.post) handler = handler.post;
- if (typeof handler !== "function") return ResponseProcessor.ACCEPT;
+ if (typeof handler !== 'function') return ResponseProcessor.ACCEPT;
}
- let { requestId, responseHeaders } = request;
+ let { requestId } = request;
let filter = browser.webRequest.filterResponseData(requestId);
let buffer = [];
@@ -89,9 +89,9 @@ class ResponseTextFilter {
buffer.push(event.data);
};
- filter.onstop = async event => {
+ filter.onstop = async unused => {
// concatenate chunks
- let size = buffer.reduce((sum, chunk, n) => sum + chunk.byteLength, 0)
+ let size = buffer.reduce((sum, chunk) => sum + chunk.byteLength, 0)
let allBytes = new Uint8Array(size);
let pos = 0;
for (let chunk of buffer) {
@@ -100,13 +100,13 @@ class ResponseTextFilter {
}
buffer = null; // allow garbage collection
if (allBytes.indexOf(0) !== -1) {
- console.debug("Warning: zeroes in bytestream, probable cached encoding mismatch.", request);
- if (request.type === "script") {
- console.debug("It's a script, trying to refetch it.");
- response.text = await (await fetch(request.url, { cache: "reload", credentials: "include" })).text();
+ console.debug('Warning: zeroes in bytestream, probable cached encoding mismatch.', request);
+ if (request.type === 'script') {
+ console.debug('It\'s a script, trying to refetch it.');
+ response.text = await (await fetch(request.url, { cache: 'reload', credentials: 'include' })).text();
} else {
- console.debug("It's a %s, trying to decode it as UTF-16.", request.type);
- response.text = new TextDecoder("utf-16be").decode(allBytes, { stream: true });
+ console.debug('It\'s a %s, trying to decode it as UTF-16.', request.type);
+ response.text = new TextDecoder('utf-16be').decode(allBytes, { stream: true });
}
} else {
response.text = metaData.decode(allBytes);
diff --git a/common/Storage.js b/common/Storage.js
index 47261c5..6254d66 100644
--- a/common/Storage.js
+++ b/common/Storage.js
@@ -23,7 +23,7 @@
A tiny wrapper around extensions storage API, supporting CSV serialization for
retro-compatibility
*/
-"use strict";
+'use strict';
var Storage = {
ARRAY: {
@@ -45,7 +45,7 @@ var Storage = {
},
async save(key, list) {
- return await browser.storage.local.set({ [key]: [...list].join(",") });
+ return await browser.storage.local.set({ [key]: [...list].join(',') });
}
}
};
@@ -68,20 +68,20 @@ class ListStore {
static inlineItem(url) {
// here we simplify and hash inline script references
- return url.startsWith("inline:") ? url
- : url.startsWith("view-source:")
- && url.replace(/^view-source:[\w-+]+:\/+([^/]+).*#line\d+/, "inline://$1#")
- .replace(/\n[^]*/, s => s.replace(/\s+/g, ' ').substring(0, 16) + "…" + hash(s.trim()));
+ return url.startsWith('inline:') ? url
+ : url.startsWith('view-source:')
+ && url.replace(/^view-source:[\w-+]+:\/+([^/]+).*#line\d+/, 'inline://$1#')
+ .replace(/\n[^]*/, s => s.replace(/\s+/g, ' ').substring(0, 16) + '…' + hash(s.trim()));
}
static hashItem(hash) {
- return hash.startsWith("(") ? hash : `(${hash})`;
+ return hash.startsWith('(') ? hash : `(${hash})`;
}
static urlItem(url) {
- let queryPos = url.indexOf("?");
+ let queryPos = url.indexOf('?');
return queryPos === -1 ? url : url.substring(0, queryPos);
}
static siteItem(url) {
- if (url.endsWith("/*")) return url;
+ if (url.endsWith('/*')) return url;
try {
return `${new URL(url).origin}/*`;
} catch (e) {
@@ -134,12 +134,12 @@ class ListStore {
}
function hash(source) {
- var shaObj = new jssha("SHA-256", "TEXT")
+ var shaObj = new jssha('SHA-256', 'TEXT')
shaObj.update(source);
- return shaObj.getHash("HEX");
+ return shaObj.getHash('HEX');
}
-if (typeof module === "object") {
+if (typeof module === 'object') {
module.exports = { ListStore, Storage, hash };
var jssha = require('jssha');
}
diff --git a/common/Test.js b/common/Test.js
index 7acbfa0..88baff2 100644
--- a/common/Test.js
+++ b/common/Test.js
@@ -19,9 +19,9 @@
* along with GNU LibreJS. If not, see <http://www.gnu.org/licenses/>.
*/
-"use strict";
+'use strict';
var Test = (() => {
- const RUNNER_URL = browser.extension.getURL("/test/SpecRunner.html");
+ const RUNNER_URL = browser.extension.getURL('/test/SpecRunner.html');
return {
/*
returns RUNNER_URL if it's a test-enabled build or an about:debugging
@@ -50,6 +50,6 @@ var Test = (() => {
}
};
})();
-if (typeof module === "object") {
+if (typeof module === 'object') {
module.exports = Test;
}
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' }),
]);
}
diff --git a/test/spec/LibreJSSpec.js b/test/spec/LibreJSSpec.js
index 1320a2a..9e96af5 100644
--- a/test/spec/LibreJSSpec.js
+++ b/test/spec/LibreJSSpec.js
@@ -18,9 +18,9 @@
* You should have received a copy of the GNU General Public License
* along with GNU LibreJS. If not, see <http://www.gnu.org/licenses/>.
*/
-"use strict";
+'use strict';
-describe("LibreJS' components", () => {
+describe('LibreJS\' components', () => {
let LibreJS = browser.extension.getBackgroundPage().LibreJS;
let license = {
id: 'GPL-3.0',
@@ -33,60 +33,60 @@ describe("LibreJS' components", () => {
magnet: 'magnet:?xt=urn:btih:2f739d935676111cfff4b4693e3816e664797050&dn=acme-1.5.txt'
};
- let trivial = "1+1";
- let nontrivial = `function nt() { document.documentElement.innerHTML=""; nt(); }`;
+ let trivial = '1+1';
+ let nontrivial = 'function nt() { document.documentElement.innerHTML=""; nt(); }';
let licensed = `// @license ${license.magnet} ${license.id}\n${nontrivial}\n// @license-end`;
let unknownLicensed = `// @license ${unknownLicense.magnet} ${unknownLicense.id}\n${nontrivial}\n// @license-end`;
let malformedLicensed = `// @license\n${nontrivial}`;
let tab, documentUrl;
beforeAll(async () => {
- let url = browser.extension.getURL("/test/resources/index.html");
+ let url = browser.extension.getURL('/test/resources/index.html');
tab = (await browser.tabs.query({ url }))[0] || (await browser.tabs.create({ url }));
documentUrl = url;
});
- describe("The whitelist/blacklist manager", () => {
+ describe('The whitelist/blacklist manager', () => {
let { ListManager, ListStore, Storage } = LibreJS;
- let lm = new ListManager(new ListStore("_test.whitelist", Storage.CSV), new ListStore("_test.blacklist", Storage.CSV), new Set());
- let forgot = ["http://formerly.whitelist.ed/", "http://formerly.blacklist.ed/"];
+ let lm = new ListManager(new ListStore('_test.whitelist', Storage.CSV), new ListStore('_test.blacklist', Storage.CSV), new Set());
+ let forgot = ['http://formerly.whitelist.ed/', 'http://formerly.blacklist.ed/'];
beforeAll(async () => {
- await lm.whitelist("https://fsf.org/*", "https://*.gnu.org/*", forgot[0]);
- await lm.blacklist("https://*.evil.gnu.org/*", "https://verybad.com/*", forgot[1]);
+ await lm.whitelist('https://fsf.org/*', 'https://*.gnu.org/*', forgot[0]);
+ await lm.blacklist('https://*.evil.gnu.org/*', 'https://verybad.com/*', forgot[1]);
});
- it("Should handle basic CRUD operations", async () => {
- expect(lm.getStatus(forgot[0])).toBe("whitelisted");
- expect(lm.getStatus(forgot[1])).toBe("blacklisted");
+ it('Should handle basic CRUD operations', async () => {
+ expect(lm.getStatus(forgot[0])).toBe('whitelisted');
+ expect(lm.getStatus(forgot[1])).toBe('blacklisted');
await lm.forget(...forgot);
for (let url of forgot) {
- expect(lm.getStatus(url)).toBe("unknown");
+ expect(lm.getStatus(url)).toBe('unknown');
}
});
- it("Should support full path wildcards", () => {
- expect(lm.getStatus("https://unknown.org")).toBe("unknown");
- expect(lm.getStatus("https://fsf.org/some/path")).toBe("whitelisted");
- expect(lm.getStatus("https://fsf.org/")).toBe("whitelisted");
- expect(lm.getStatus("https://fsf.org")).toBe("whitelisted");
- expect(lm.getStatus("https://subdomain.fsf.org")).toBe("unknown");
- expect(lm.getStatus("https://verybad.com/some/other/path?with=querystring")).toBe("blacklisted");
+ it('Should support full path wildcards', () => {
+ expect(lm.getStatus('https://unknown.org')).toBe('unknown');
+ expect(lm.getStatus('https://fsf.org/some/path')).toBe('whitelisted');
+ expect(lm.getStatus('https://fsf.org/')).toBe('whitelisted');
+ expect(lm.getStatus('https://fsf.org')).toBe('whitelisted');
+ expect(lm.getStatus('https://subdomain.fsf.org')).toBe('unknown');
+ expect(lm.getStatus('https://verybad.com/some/other/path?with=querystring')).toBe('blacklisted');
});
- it("Should support subdomain wildcards", () => {
- expect(lm.getStatus("https://gnu.org")).toBe("whitelisted");
- expect(lm.getStatus("https://www.gnu.org")).toBe("whitelisted");
- expect(lm.getStatus("https://evil.gnu.org")).toBe("blacklisted");
- expect(lm.getStatus("https://more.evil.gnu.org")).toBe("blacklisted");
- expect(lm.getStatus("https://more.evil.gnu.org/some/evil/path?too")).toBe("blacklisted");
+ it('Should support subdomain wildcards', () => {
+ expect(lm.getStatus('https://gnu.org')).toBe('whitelisted');
+ expect(lm.getStatus('https://www.gnu.org')).toBe('whitelisted');
+ expect(lm.getStatus('https://evil.gnu.org')).toBe('blacklisted');
+ expect(lm.getStatus('https://more.evil.gnu.org')).toBe('blacklisted');
+ expect(lm.getStatus('https://more.evil.gnu.org/some/evil/path?too')).toBe('blacklisted');
});
})
- describe("The external script source processor", () => {
- let url = "https://www.gnu.org/mock-script.js";
+ describe('The external script source processor', () => {
+ let url = 'https://www.gnu.org/mock-script.js';
let processScript = async (source, whitelisted = false) =>
await LibreJS.handle_script({
@@ -94,125 +94,125 @@ describe("LibreJS' components", () => {
request: { url, tabId: tab.id, documentUrl, frameId: 0 },
}, whitelisted);
- it("should accept whitelisted scripts", async () => {
+ it('should accept whitelisted scripts', async () => {
expect(await processScript(nontrivial, true) || nontrivial).toContain(nontrivial);
});
- it("should block trivial scripts too", async () => {
+ it('should block trivial scripts too', async () => {
let processed = await processScript(trivial);
expect(processed || trivial).not.toContain(trivial);
});
- it("should block non-trivial scripts", async () => {
+ it('should block non-trivial scripts', async () => {
let processed = await processScript(nontrivial);
expect(processed || nontrivial).not.toContain(nontrivial);
});
- it("should accept scripts with known free license tags", async () => {
+ it('should accept scripts with known free license tags', async () => {
let processed = await processScript(licensed);
expect(processed || licensed).toContain(nontrivial);
});
- it("should block scripts with unknown license tags", async () => {
+ it('should block scripts with unknown license tags', async () => {
let processed = await processScript(unknownLicensed);
expect(processed).not.toContain(nontrivial);
});
- it("should block scripts with malformed license tags", async () => {
+ it('should block scripts with malformed license tags', async () => {
let processed = await processScript(malformedLicensed);
expect(processed).not.toContain(nontrivial);
});
});
- describe("The HTML processor", () => {
+ describe('The HTML processor', () => {
let processHtml =
async (html, whitelisted = false) =>
LibreJS.editHtml(html, tab.url, tab.id, 0, whitelisted);
- let addScript = (html, script, before = "</head>") =>
+ let addScript = (html, script, before = '</head>') =>
html.replace(before, `<script>${script}</script>${before}`);
- let addToBody = (html, fragment) => html.replace("</body>", `${fragment}</body>`);
+ let addToBody = (html, fragment) => html.replace('</body>', `${fragment}</body>`);
let jsUrl = js => `javascript:${encodeURIComponent(js)}`;
- function extractScripts(html, def = "") {
+ function extractScripts(html, def = '') {
let matches = html && html.match(/<script>[^]*?<\/script>/g);
- return matches && matches.join("") || def;
+ return matches && matches.join('') || def;
}
let html, nontrivialInHtml;
beforeAll(async () => {
html = (await browser.tabs.executeScript(tab.id, {
- runAt: "document_start",
- code: "document.documentElement.outerHTML"
+ runAt: 'document_start',
+ code: 'document.documentElement.outerHTML'
}))[0];
nontrivialInHtml = addScript(html, nontrivial);
});
- it("should not modify scriptless documents", async () => {
+ it('should not modify scriptless documents', async () => {
expect(await processHtml(html)).toBeNull();
});
- it("should not modify whitelisted documents", async () => {
+ it('should not modify whitelisted documents', async () => {
expect(await processHtml(nontrivialInHtml, true)).toBeNull();
});
- it("should accept trivial scripts", async () => {
+ it('should accept trivial scripts', async () => {
let trivialInHtml = addScript(html, trivial);
let processed = await processHtml(trivialInHtml);
expect(extractScripts(processed, trivial)).toContain(trivial);
});
- it("should block non-trivial scripts", async () => {
+ it('should block non-trivial scripts', async () => {
let processed = await processHtml(nontrivialInHtml);
expect(extractScripts(processed, nontrivial)).not.toContain(nontrivial);
});
- it("should accept scripts with known free license tags", async () => {
+ it('should accept scripts with known free license tags', async () => {
let licensedInHtml = addScript(html, licensed);
let processed = await processHtml(licensedInHtml);
expect(extractScripts(processed, licensed)).toContain(nontrivial);
});
- it("should block scripts with unknown license tags", async () => {
+ it('should block scripts with unknown license tags', async () => {
let unknownInHtml = addScript(html, unknownLicensed);
let processed = await processHtml(unknownInHtml);
expect(extractScripts(processed, nontrivial)).not.toContain(nontrivial);
});
- it("should block scripts with malformed license tags", async () => {
+ it('should block scripts with malformed license tags', async () => {
let malformedInHtml = addScript(html, malformedLicensed);
let processed = await processHtml(malformedInHtml);
expect(extractScripts(processed, nontrivial)).not.toContain(nontrivial);
});
- it("should accept scripts on globally licensed pages", async () => {
+ it('should accept scripts on globally licensed pages', async () => {
let globalLicense = `/* @licstart The following is the entire license notice
for the JavaScript code in this page.
-- Some free license --
@licend The above is the entire license notice for the JavaScript code in this page. */`;
- let licensed = addScript(nontrivialInHtml, globalLicense, "<script>");
+ let licensed = addScript(nontrivialInHtml, globalLicense, '<script>');
let processed = await processHtml(html);
expect(extractScripts(processed, licensed)).toContain(nontrivial);
});
- it("should discriminate trivial, non-trivial and licensed mixed on the same page", async () => {
+ it('should discriminate trivial, non-trivial and licensed mixed on the same page', async () => {
let mixedPage = addScript(addScript(nontrivialInHtml, trivial), licensed);
let processed = await processHtml(mixedPage);
expect(processed).not.toBeNull();
let scripts = extractScripts(processed, nontrivial);
expect(scripts).toContain(trivial);
expect(scripts).toContain(licensed);
- expect(scripts.replace(licensed, "")).not.toContain(nontrivial);
+ expect(scripts.replace(licensed, '')).not.toContain(nontrivial);
});
- it("should correctly process (de)duplicated inline scripts", async () => {
+ it('should correctly process (de)duplicated inline scripts', async () => {
let trivialAsUrl = jsUrl(trivial);
let nontrivialAsUrl = jsUrl(nontrivial);
let a = (url, label) => `<a href="${url}">${label}</a>`;
- let mixedPage = `<body></body>`;
+ let mixedPage = '<body></body>';
for (let dup = 0; dup < 3; dup++) {
mixedPage = addToBody(mixedPage, a(trivialAsUrl, `Trivial #${dup}`));
mixedPage = addToBody(mixedPage, a(nontrivialAsUrl, `Nontrivial #${dup}`));
@@ -223,8 +223,8 @@ describe("LibreJS' components", () => {
expect(processed).not.toContain(nontrivialAsUrl);
});
- it("should force displaying NOSCRIPT elements (except those with @data-librejs-nodisplay) where scripts have been blocked", async () => {
- let noscriptContent = "I'm NOSCRIPT content";
+ it('should force displaying NOSCRIPT elements (except those with @data-librejs-nodisplay) where scripts have been blocked', async () => {
+ let noscriptContent = 'I\'m NOSCRIPT content';
let asNoscript = `<noscript>${noscriptContent}</noscript>`;
let asNodisplay = `<noscript data-librejs-nodisplay>${noscriptContent}</noscript>`;
let asSpan = `<span>${noscriptContent}</span>`;
@@ -235,11 +235,11 @@ describe("LibreJS' components", () => {
expect(processed).not.toContain(asNodisplay);
});
- it("should always force displaying @data-librejs-display elements", async () => {
- let content = "I'm FORCED content";
+ it('should always force displaying @data-librejs-display elements', async () => {
+ let content = 'I\'m FORCED content';
let asDisplay = `<span data-librejs-display>${content}</span>`;
let asSpan = `<span>${content}</span>`;
- for (let page of [nontrivialInHtml, "<body></body>"]) {
+ for (let page of [nontrivialInHtml, '<body></body>']) {
page = addToBody(page, asDisplay);
let processed = await processHtml(page);
expect(processed).not.toContain(asDisplay);
@@ -248,7 +248,7 @@ describe("LibreJS' components", () => {
});
});
- describe("The external (Web Labels) license checker", () => {
+ describe('The external (Web Labels) license checker', () => {
let { ExternalLicenses } = LibreJS;
let check;
@@ -257,27 +257,27 @@ describe("LibreJS' components", () => {
let resolve = url => new URL(url, documentUrl).href;
check = async url => await ExternalLicenses.check(Object.assign({ url: resolve(url) }, args));
await browser.tabs.executeScript(tab.id, {
- file: "/content/externalLicenseChecker.js"
+ file: '/content/externalLicenseChecker.js'
});
});
- it("should recognize free licenses", async () => {
- let scriptInfo = await check("jquery.js");
+ it('should recognize free licenses', async () => {
+ let scriptInfo = await check('jquery.js');
console.debug(scriptInfo);
expect(scriptInfo.free).toBeTruthy();
});
- it("should accept scripts if any of multiple licenses is free", async () => {
- let scriptInfo = await check("app-trilicensed.js");
+ it('should accept scripts if any of multiple licenses is free', async () => {
+ let scriptInfo = await check('app-trilicensed.js');
console.debug(scriptInfo);
expect(scriptInfo.free).toBeTruthy();
});
- it("should block scripts declaring only proprietary license(s)", async () => {
- let scriptInfo = await check("proprietary.js");
+ it('should block scripts declaring only proprietary license(s)', async () => {
+ let scriptInfo = await check('proprietary.js');
console.debug(scriptInfo);
expect(scriptInfo.free).toBeFalsy();
});
- it("should block scripts not declaring any license", async () => {
- let scriptInfo = await check("tracker.js");
+ it('should block scripts not declaring any license', async () => {
+ let scriptInfo = await check('tracker.js');
console.debug(scriptInfo);
expect(scriptInfo).toBeNull();
});