| |
@@ -22,10 +22,9 @@
|
| |
|
| |
var acorn = require('acorn');
|
| |
var acornLoose = require('acorn-loose');
|
| |
- var jssha = require('jssha');
|
| |
var legacy_license_lib = require("./legacy_license_check.js");
|
| |
var {ResponseProcessor} = require("./bg/ResponseProcessor");
|
| |
- var {Storage, ListStore} = require("./common/Storage");
|
| |
+ var {Storage, ListStore, hash} = require("./common/Storage");
|
| |
var {ListManager} = require("./bg/ListManager");
|
| |
var {ExternalLicenses} = require("./bg/ExternalLicenses");
|
| |
|
| |
@@ -51,16 +50,6 @@
|
| |
}
|
| |
}
|
| |
|
| |
- /**
|
| |
- * Wrapper around crypto lib
|
| |
- *
|
| |
- */
|
| |
- function hash(source){
|
| |
- var shaObj = new jssha("SHA-256","TEXT")
|
| |
- shaObj.update(source);
|
| |
- return shaObj.getHash("HEX");
|
| |
- }
|
| |
-
|
| |
/*
|
| |
NONTRIVIAL THINGS:
|
| |
- Fetch
|
| |
@@ -336,7 +325,11 @@
|
| |
for (let action of ["whitelist", "blacklist", "forget"]) {
|
| |
if (m[action]) {
|
| |
let [key] = m[action];
|
| |
- if (m.site) key = ListStore.siteItem(key);
|
| |
+ if (m.site) {
|
| |
+ key = ListStore.siteItem(key);
|
| |
+ } else {
|
| |
+ key = ListStore.inlineItem(key) || key;
|
| |
+ }
|
| |
await listManager[action](key);
|
| |
update = true;
|
| |
}
|
| |
@@ -776,23 +769,20 @@
|
| |
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 scriptSource = verdict ? response : editedSource;
|
| |
switch(category) {
|
| |
case "blacklisted":
|
| |
- if (response.startsWith("javascript:"))
|
| |
- return result(`# LibreJS: script ${category} by user.`);
|
| |
- else
|
| |
- return result(`/* LibreJS: script ${category} by user. */`);
|
| |
+ editedSource = `/* LibreJS: script ${category} by user. */`;
|
| |
+ return result(response.startsWith("javascript:")
|
| |
+ ? `javascript:void(${encodeURIComponent(editedSource)})` : editedSource);
|
| |
case "whitelisted":
|
| |
- if (response.startsWith("javascript:"))
|
| |
- return result(scriptSource);
|
| |
- else
|
| |
- return result(`/* LibreJS: script ${category} by user. */\n${scriptSource}`);
|
| |
+ return result(response.startsWith("javascript:")
|
| |
+ ? response : `/* LibreJS: script ${category} by user. */\n${response}`);
|
| |
default:
|
| |
- if (response.startsWith("javascript:"))
|
| |
- return result(scriptSource);
|
| |
- else
|
| |
- return result(`/* LibreJS: script ${category}. */\n${scriptSource}`);
|
| |
+ let scriptSource = verdict ? response : editedSource;
|
| |
+ return result(response.startsWith("javascript:")
|
| |
+ ? (verdict ? scriptSource : `javascript:void(/* ${scriptSource} */)`)
|
| |
+ : `/* LibreJS: script ${category}. */\n${scriptSource}`
|
| |
+ );
|
| |
}
|
| |
}
|
| |
|
| |
@@ -1053,17 +1043,20 @@
|
| |
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");
|
| |
- let line = 0;
|
| |
+ let line, extras;
|
| |
if (meta_element) {
|
| |
line = findLine(/id\s*=\s*['"]?LibreJS-info\b/gi);
|
| |
+ extras = "(0)";
|
| |
} else if (license) {
|
| |
line = html.substring(0, html.indexOf(first_script_src)).split(/\n/).length;
|
| |
+ extras = "\n" + first_script_src;
|
| |
}
|
| |
- let viewUrl = line ? `view-source:${documentUrl}#line${line}(<${meta_element ? meta_element.tagName : "SCRIPT"}>)(0)` : url;
|
| |
+ 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}`]});
|
| |
// 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;
|
| |
@@ -1071,21 +1064,27 @@
|
| |
for (let element of html_doc.all) {
|
| |
let line = -1;
|
| |
for (let attr of element.attributes) {
|
| |
- if (attr.name.startsWith("on") || (attr.name === "href" && attr.value.toLowerCase().startsWith("javascript:"))){
|
| |
+ let {name, value} = attr;
|
| |
+ value = value.trim();
|
| |
+ if (name.startsWith("on") || (name === "href" && value.toLowerCase().startsWith("javascript:"))){
|
| |
intrinsecindex++;
|
| |
if (line === -1) {
|
| |
line = findLine(intrinsicFinder);
|
| |
}
|
| |
try {
|
| |
- let url = `view-source:${documentUrl}#line${line}(<${element.tagName} ${attr.name}>)(${intrinsicIndex})`;
|
| |
- let edited = await get_script(attr.value, url, tabId, whitelist.contains(url));
|
| |
- if (edited) {
|
| |
- let value = edited;
|
| |
- if (value !== attr.value) {
|
| |
- modified = true;
|
| |
- attr.value = value;
|
| |
- }
|
| |
- }
|
| |
+ let key = `<${element.tagName} ${name}="${value}">`;
|
| |
+ let edited;
|
| |
+ if (dejaVu.has(key)) {
|
| |
+ edited = dejaVu.get(key);
|
| |
+ } else {
|
| |
+ let url = `view-source:${documentUrl}#line${line}(<${element.tagName} ${name}>)\n${value.trim()}`;
|
| |
+ if (name === "href") value = decodeURIComponent(value);
|
| |
+ edited = await get_script(value, url, tabId, whitelist.contains(url)); dejaVu.set(key, edited);
|
| |
+ }
|
| |
+ if (edited && edited !== value) {
|
| |
+ modified = true;
|
| |
+ attr.value = edited;
|
| |
+ }
|
| |
} catch (e) {
|
| |
console.error(e);
|
| |
}
|
| |
@@ -1099,14 +1098,19 @@
|
| |
let script = scripts[i];
|
| |
let line = findLine(scriptFinder);
|
| |
if (!script.src && !(script.type && script.type !== "text/javascript")) {
|
| |
- let source = script.textContent;
|
| |
- let url = `view-source:${documentUrl}#line${line}(<SCRIPT>)(${i})`;
|
| |
- let edited = await get_script(source, url, tabId, whitelisted, i);
|
| |
- if (edited) {
|
| |
- let edited_source = edited[0];
|
| |
- let unedited_source = source.trim();
|
| |
- if (edited_source.trim() !== unedited_source) {
|
| |
- script.textContent = edited_source;
|
| |
+ let source = script.textContent.trim();
|
| |
+ let editedSource;
|
| |
+ if (dejaVu.has(source)) {
|
| |
+ editedSource = dejaVu.get(source);
|
| |
+ } else {
|
| |
+ let url = `view-source:${documentUrl}#line${line}(<SCRIPT>)\n${source}`;
|
| |
+ let edited = await get_script(source, url, tabId, whitelisted, i);
|
| |
+ editedSource = edited && edited[0].trim();
|
| |
+ dejaVu.set(url, editedSource);
|
| |
+ }
|
| |
+ if (editedSource) {
|
| |
+ if (source !== editedSource) {
|
| |
+ script.textContent = editedSource;
|
| |
modified = modifiedInline = true;
|
| |
}
|
| |
}
|
| |
As discussed in our last meeting, this provides a collapsed expandable widget to inspect the intercepted inline scripts (both attribute values and script blocks) in the main popup UI.
The inline scripts are also de-duplicated while scanned, providing an UI entry for the first instance only (to avoid unnecessary clutter) and caching the processing results (to avoid unnecessary CPU work).
Tests for (de)duplicate inline scripts in attributes have been provided as well.