aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bg/ResponseProcessor.js35
-rw-r--r--main_background.js173
2 files changed, 111 insertions, 97 deletions
diff --git a/bg/ResponseProcessor.js b/bg/ResponseProcessor.js
index 1af8cd7..4443d90 100644
--- a/bg/ResponseProcessor.js
+++ b/bg/ResponseProcessor.js
@@ -33,7 +33,7 @@ class ResponseProcessor {
static install(handler, types = ["main_frame", "sub_frame", "script"]) {
if (listeners.has(handler)) return false;
- let listener =
+ let listener =
async request => await new ResponseTextFilter(request).process(handler);
listeners.set(handler, listener);
webRequestEvent.addListener(
@@ -80,7 +80,7 @@ class ResponseTextFilter {
if (handler.post) handler = handler.post;
if (typeof handler !== "function") ResponseProcessor.ACCEPT;
}
-
+
let {requestId, responseHeaders} = request;
let filter = browser.webRequest.filterResponseData(requestId);
let buffer = [];
@@ -90,11 +90,29 @@ class ResponseTextFilter {
};
filter.onstop = async event => {
- let decoder = metaData.createDecoder();
+
let params = {stream: true};
- response.text = buffer.map(
- chunk => decoder.decode(chunk, params))
- .join('');
+ // concatenate chunks
+ let size = buffer.reduce((sum, chunk, n) => sum + chunk.byteLength, 0)
+ let allBytes = new Uint8Array(size);
+ let pos = 0;
+ for (let chunk of buffer) {
+ allBytes.set(new Uint8Array(chunk), pos);
+ pos += chunk.byteLength;
+ }
+ 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();
+ } else {
+ console.debug("It's a %s, trying to decode it as UTF-16.", request.type);
+ response.text = new TextDecoder("utf-16be").decode(allBytes);
+ }
+ } else {
+ response.text = metaData.createDecoder().decode(allBytes, {stream: true});
+ }
let editedText = null;
try {
editedText = await handler(response);
@@ -108,10 +126,9 @@ class ResponseTextFilter {
filter.write(new TextEncoder().encode(editedText));
} else {
// ... otherwise pass all the raw bytes through
- for (let chunk of buffer) filter.write(chunk);
+ filter.write(allBytes);
}
-
- filter.disconnect();
+ filter.close();
}
return metaData.forceUTF8() ? {responseHeaders} : ResponseProcessor.ACCEPT;;
diff --git a/main_background.js b/main_background.js
index f9c5dac..cdc987e 100644
--- a/main_background.js
+++ b/main_background.js
@@ -599,6 +599,8 @@ function full_evaluate(script){
* This can only determine if a script is bad, not if it's good
*
* If it passes the intitial pass, it runs the full pass and returns the result
+
+* It returns an array of [flag (boolean, false if "bad"), reason (string, human readable report)]
*
*/
function evaluate(script,name){
@@ -636,27 +638,24 @@ function evaluate(script,name){
return [flag,reason];
}
- var final = full_evaluate(script);
-// final[1] = final[1] + "<br>";
- return final;
+ return full_evaluate(script);
}
-function license_valid(matches){
- if(matches.length != 4){
- return [false, "malformed or unrecognized license tag"];
- }
- if(matches[1] != "@license"){
- return [false, "malformed or unrecognized license tag"];
+function validateLicense(matches) {
+ if (!(Array.isArray(matches) && matches.length === 4)){
+ return [false, "Malformed or unrecognized license tag."];
}
- if(licenses[matches[3]] === undefined){
- return [false, "malformed or unrecognized license tag"];
+ let [all, tag, link, id] = matches;
+ let license = licenses[id];
+ if(!license){
+ return [false, `Unrecognized license "${id}"`];
}
- if(licenses[matches[3]]["Magnet link"] != matches[2]){
- return [false, "malformed or unrecognized license tag"];
+ if(license["Magnet link"] != link){
+ return [false, `License magnet link does not match for "${id}".`];
}
- return [true,"Recognized license as '"+matches[3]+"'<br>"];
+ return [true, `Recognized license: "${id}".`];
}
/**
*
@@ -669,91 +668,89 @@ function license_valid(matches){
* reason text
* ]
*/
-function license_read(script_src, name, external = false){
-
- var reason_text = "";
-
- var edited_src = "";
- var unedited_src = script_src;
- var nontrivial_status;
- var parts_denied = false;
- var parts_accepted = false;
- var license = legacy_license_lib.check(script_src);
- if(license != false){
- return [true,script_src,"Licensed under: "+license];
+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(script_src))){
- return [true,script_src,"Common script known to be free software."];
+ if (listManager.builtInHashes.has(hash(scriptSrc))){
+ return [true, scriptSrc, "Common script known to be free software."];
}
- while(true){ // TODO: refactor me
- // TODO: support multiline comments
- var matches = /\/[\/\*]\s*?(@license)\s([\S]+)\s([\S]+$)/gm.exec(unedited_src);
- var empty = /[^\s]/gm.exec(unedited_src);
- if(empty == null){
- return [true,edited_src,reason_text];
+
+ 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
}
- if(matches == null){
- if (external)
- return [false,edited_src,"External script with no known license."];
- else
- nontrivial_status = evaluate(unedited_src,name);
- if(nontrivial_status[0] == true){
- parts_accepted = true;
- edited_src += unedited_src;
- } else{
- parts_denied = true;
- edited_src += "\n/*\nLIBREJS BLOCKED:"+nontrivial_status[1]+"\n*/\n";
- }
- reason_text += "\n" + nontrivial_status[1];
+ let [trivial, message] = external ?
+ [false, "External script with no known license"]
+ : evaluate(s, name);
+ if (trivial) {
+ partsAccepted = true;
+ editedSrc += s;
+ } else {
+ partsDenied = true;
+ editedSrc += `\n/*\nLIBREJS BLOCKED: ${message}\n*/\n`;
+ }
+ reason += `\n${message}`;
+ return trivial;
+ }
- if(parts_denied == true && parts_accepted == true){
- reason_text = "Script was determined partly non-trivial after editing. (check source for details)\n"+reason_text;
- }
- if(parts_denied == true && parts_accepted == false){
- return [false,edited_src,reason_text];
- }
- else return [true,edited_src,reason_text];
+ 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));
}
- // sponge
- dbg_print("undedited_src:");
- dbg_print(unedited_src);
- dbg_print(matches);
- dbg_print("chopping at " + matches["index"] + ".");
- var before = unedited_src.substring(0,matches["index"]);
- // sponge
- dbg_print("before:");
- dbg_print(before);
- if (external)
- nontrivial_status = [true, "External script with no known license"]
- else
- nontrivial_status = evaluate(before,name);
- if(nontrivial_status[0] == true){
- parts_accepted = true;
- edited_src += before;
- } else{
- parts_denied = true;
- edited_src += "\n/*\nLIBREJS BLOCKED:"+nontrivial_status[1]+"\n*/\n";
+ // 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];
}
- unedited_src = unedited_src.substr(matches["index"],unedited_src.length);
- // TODO: support multiline comments
- var matches_end = /\/\/\s*?(@license-end)/gm.exec(unedited_src);
- if(matches_end == null){
- dbg_print("ERROR: @license with no @license-end");
- return [false,"\n/*\n ERROR: @license with no @license-end \n*/\n","ERROR: @license with no @license-end"];
+
+ let closureEndIndex = closureMatch.index + closureMatch[0].length;
+ let commentEndOffset = uneditedSrc.substring(closureEndIndex).indexOf(closureMatch[1] === "*" ? "*/" : "\n");
+ if (commentEndOffset !== -1) {
+ closureEndIndex += commentEndOffset;
}
- var endtag_end_index = matches_end["index"]+matches_end[0].length;
- var license_res = license_valid(matches);
- if(license_res[0] == true){
- edited_src = edited_src + unedited_src.substr(0,endtag_end_index);
- reason_text += "\n" + license_res[1];
- } else{
- edited_src = edited_src + "\n/*\n"+license_res[1]+"\n*/\n";
- reason_text += "\n" + license_res[1];
+
+ 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
- unedited_src = unedited_src.substr(endtag_end_index,unedited_src.length);
+ 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];
}
/* *********************************************************************************************** */