aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--eval_test.js19
-rw-r--r--main_background.js388
2 files changed, 365 insertions, 42 deletions
diff --git a/eval_test.js b/eval_test.js
index e3550ff..fa508f6 100644
--- a/eval_test.js
+++ b/eval_test.js
@@ -162,7 +162,6 @@ function get_final_page(html_string, callback){
*
*/
function evaluate(script,name){
-
function reserved_object_regex(object){
// Matches use of object as a variable
@@ -259,7 +258,6 @@ function get_final_page(html_string, callback){
// Once Javascript has been "judged", remove it from here
var unedited_src = script_src;
var first = true;
- var watchdog = 0;
while(true){
if(first){
first = false;
@@ -287,15 +285,12 @@ function get_final_page(html_string, callback){
matches_end = /^(@license-end)/gm.exec(unedited_src);
if(matches_end == null){
console.log("ERROR: @license with no @license-end");
- return false;
+ return [false,"ERROR: @license with no @license-end"];
}
var endtag_end_index = matches_end["index"]+matches_end[0].length;
// accept next tag if its license is good.
if(license_valid(matches)){
edited_src = edited_src + unedited_src.substr(0,endtag_end_index);
- } else{
- console.log("Error: invalid license tag.");
- return false;
}
// Remove the next tag (it will be in edited_src if it was accepted)
unedited_src = unedited_src.substr(endtag_end_index,unedited_src.length);
@@ -303,13 +298,6 @@ function get_final_page(html_string, callback){
//console.log("%c"+unedited_src,"color:red;");
//console.log("Current output:");
//console.log("%c"+edited_src,"color:green;");
-
- // TODO: this is here to prevent infinite loops, should be removed eventually
- watchdog++;
- if(watchdog > 20){
- console.log("%c !!!!!WARNING!!!!! Watchdog > 20.","color:red");
- return false;
- }
}
}
/**
@@ -461,8 +449,9 @@ function get_final_page(html_string, callback){
check_done();
}
- // "main" for the script analyzer
- // called when invoked by the button
+ /*
+ * Basically just calls license_read() on all the Javascript in html_source
+ */
function analyze(html_source,callback){
// TODO: Call get_whitelisted_status on this page's URL
diff --git a/main_background.js b/main_background.js
index d249f3b..ae430c1 100644
--- a/main_background.js
+++ b/main_background.js
@@ -1,6 +1,159 @@
console.debug("main_background.js");
/**
+* This file is the "skeleton" of the final system to determine
+* if a script is accepted or blocked.
+*
+* Some assets taken from script_detector.js
+*
+*/
+
+// the list of all available event attributes
+var intrinsicEvents = [
+ "onload",
+ "onunload",
+ "onclick",
+ "ondblclick",
+ "onmousedown",
+ "onmouseup",
+ "onmouseover",
+ "onmousemove",
+ "onmouseout",
+ "onfocus",
+ "onblur",
+ "onkeypress",
+ "onkeydown",
+ "onkeyup",
+ "onsubmit",
+ "onreset",
+ "onselect",
+ "onchange"
+];
+/*
+ NONTRIVIAL THINGS:
+ - Fetch
+ - XMLhttpRequest
+ - eval()
+ - ?
+ JAVASCRIPT CAN BE FOUND IN:
+ - Event handlers (onclick, onload, onsubmit, etc.)
+ - <script>JS</script>
+ - <script src="/JS.js"></script>
+ 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_
+ - <table id="jslicense-labels1"><table> which may be linked to by a link tag identified by rel="jslicense" or data-jslicense="1"
+ - In the first script tag, declare the license with @licstart/@licend
+*/
+
+var licenses = {
+ 'Apache-2.0':{
+ 'URL': 'http://www.apache.org/licenses/LICENSE-2.0',
+ 'Magnet link': 'magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt'
+ },
+ // No identifier was present in documentation
+ 'Artistic-2.0':{
+ 'URL': 'http://www.perlfoundation.org/artistic_license_2_0',
+ 'Magnet link': 'magnet:?xt=urn:btih:54fd2283f9dbdf29466d2df1a98bf8f65cafe314&dn=artistic-2.0.txt'
+ },
+ // No identifier was present in documentation
+ 'Boost':{
+ 'URL': 'http://www.boost.org/LICENSE_1_0.txt',
+ 'Magnet link': 'magnet:?xt=urn:btih:89a97c535628232f2f3888c2b7b8ffd4c078cec0&dn=Boost-1.0.txt'
+ },
+ // No identifier was present in documentation
+ 'BSD-3-Clause':{
+ 'URL': 'http://opensource.org/licenses/BSD-3-Clause',
+ 'Magnet link': 'magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt',
+ },
+ 'CPAL-1.0':{
+ 'URL': 'http://opensource.org/licenses/cpal_1.0',
+ 'Magnet link': 'magnet:?xt=urn:btih:84143bc45939fc8fa42921d619a95462c2031c5c&dn=cpal-1.0.txt'
+ },
+ 'CC0-1.0':{
+ 'URL': 'http://creativecommons.org/publicdomain/zero/1.0/legalcode',
+ 'Magnet link': 'magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt'
+ },
+ 'EPL-1.0':{
+ 'URL': 'http://www.eclipse.org/legal/epl-v10.html',
+ 'Magnet link': 'magnet:?xt=urn:btih:4c6a2ad0018cd461e9b0fc44e1b340d2c1828b22&dn=epl-1.0.txt'
+ },
+ 'Expat':{
+ 'URL': 'http://www.jclark.com/xml/copying.txt',
+ 'Magnet link': 'magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt'
+ },
+ 'FreeBSD':{
+ 'URL': 'http://www.freebsd.org/copyright/freebsd-license.html',
+ 'Magnet link': 'magnet:?xt=urn:btih:87f119ba0b429ba17a44b4bffcab33165ebdacc0&dn=freebsd.txt'
+ },
+ 'GPL-2.0':{
+ 'URL': 'http://www.gnu.org/licenses/gpl-2.0.html',
+ 'Magnet link': 'magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt'
+ },
+ 'GPL-3.0':{
+ 'URL': 'http://www.gnu.org/licenses/gpl-3.0.html',
+ 'Magnet link': 'magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt'
+ },
+ 'LGPL-2.1':{
+ 'URL': 'http://www.gnu.org/licenses/lgpl-2.1.html',
+ 'Magnet link': 'magnet:?xt=urn:btih:5de60da917303dbfad4f93fb1b985ced5a89eac2&dn=lgpl-2.1.txt'
+ },
+ 'LGPL-3.0':{
+ 'URL': 'http://www.gnu.org/licenses/lgpl-3.0.html',
+ 'Magnet link': 'magnet:?xt=urn:btih:0ef1b8170b3b615170ff270def6427c317705f85&dn=lgpl-3.0.txt'
+ },
+ 'AGPL-3.0':{
+ 'URL': 'http://www.gnu.org/licenses/agpl-3.0.html',
+ 'Magnet link': 'magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt'
+ },
+ 'ISC':{
+ 'URL': 'https://www.isc.org/downloads/software-support-policy/isc-license/',
+ 'Magnet link': 'magnet:?xt=urn:btih:b8999bbaf509c08d127678643c515b9ab0836bae&dn=ISC.txt'
+ },
+ 'MPL-2.0':{
+ 'URL': 'http://www.mozilla.org/MPL/2.0',
+ 'Magnet link': 'magnet:?xt=urn:btih:3877d6d54b3accd4bc32f8a48bf32ebc0901502a&dn=mpl-2.0.txt'
+ },
+ // "Public domain is not a license"
+ // Replace with CC0?
+ 'Public-Domain':{
+ 'URL': 'https://www.gnu.org/licenses/license-list.html#PublicDomain',
+ 'Magnet link': 'magnet:?xt=urn:btih:e95b018ef3580986a04669f1b5879592219e2a7a&dn=public-domain.txt'
+ },
+ 'UPL-1.0': {
+ 'URL': 'https://oss.oracle.com/licenses/upl/',
+ 'Magnet link': 'magnet:?xt=urn:btih:478974f4d41c3fa84c4befba25f283527fad107d&dn=upl-1.0.txt'
+ },
+ 'WTFPL': {
+ 'URL': 'http://www.wtfpl.net/txt/copying/',
+ 'Magnet link': 'magnet:?xt=urn:btih:723febf9f6185544f57f0660a41489c7d6b4931b&dn=wtfpl.txt'
+ },
+ 'Unlicense':{
+ 'URL': 'http://unlicense.org/UNLICENSE',
+ 'Magnet link': 'magnet:?xt=urn:btih:5ac446d35272cc2e4e85e4325b146d0b7ca8f50c&dn=unlicense.txt'
+ },
+ // No identifier was present in documentation
+ 'X11':{
+ 'URL': 'http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3',
+ 'Magnet link': 'magnet:?xt=urn:btih:5305d91886084f776adcf57509a648432709a7c7&dn=x11.txt'
+ },
+ // Picked one of the two links that were there
+ 'Modified-BSD':{
+ 'URL': 'http://www.xfree86.org/current/LICENSE4.html',
+ 'Magnet link': 'magnet:?xt=urn:btih:12f2ec9e8de2a3b0002a33d518d6010cc8ab2ae9&dn=xfree86.txt'
+ }
+}
+
+// Objects which could be used to do nontrivial things
+// Bracket suffix notation could still be exploited to get some of these objects
+var reserved_objects = [
+ "fetch",
+ "XMLHttpRequest",
+ "chrome", // only on chrome
+ "browser", // only on firefox
+ "eval"
+];
+/**
*
* Sets global variable "webex" to either "chrome" or "browser" for
* use on Chrome or a Firefox variant.
@@ -112,7 +265,6 @@ function debug_print_local(){
* NOTE: This WILL break if you provide inconsistent URLs to it.
* Make sure it will use the right URL when refering to a certain script.
*
-*
*/
function update_popup(tab_id,blocked_info_arg,update=false){
var new_blocked_data;
@@ -401,47 +553,227 @@ function get_data_url(blob){
}
/* *********************************************************************************************** */
+// (This is part of eval_test.js with a few console.logs/comments removed)
+
+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,"");
+ console.log("------evaluation results for "+ name +"------");
+ console.log("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(script);
+ if(res != null){
+ console.log("%c fail","color:red;");
+ flag = false;
+ reason = "Script uses a reserved object (" + reserved_objects[i] + ")";
+ }
+ }
+ if(flag){
+ console.log("%c pass","color:green;");
+ }
+ // If flag is set true at this point, the script is trivial
+ if(flag){
+ reason = "Script was determined to be trivial.";
+ }
+ return [flag,reason];
+}
+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"];
+ }
+ if(licenses[matches[3]] === undefined){
+ return [false, "malformed or unrecognized license tag"];
+ }
+ if(licenses[matches[3]]["Magnet link"] != matches[2]){
+ return [false, "malformed or unrecognized license tag"];
+ }
+ return [true,"Recognized license as '"+matches[3]+"'"];
+}
+/**
+*
+* Evaluates the content of a script (license, if it is non-trivial)
+*
+* Returns
+* [
+* true (accepted) or false (denied),
+* edited content,
+* reason text
+* ]
+*/
+function license_read(script_src,name){
+
+ var reason_text = "";
+
+ var edited_src = "";
+ var unedited_src = script_src;
+ var nontrivial_status;
+ var parts_denied = false;
+ var parts_accepted = false;
+ while(true){
+ // TODO: support multiline comments
+ var matches = /\/\/\s*?(@license)\s([\S]+)\s([\S]+$)/gm.exec(unedited_src);
+ if(matches == null){
+ 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];
+
+ 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];
+ }
+ return [true,edited_src,reason_text];
+
+ }
+ console.log("Found a license tag");
+ var before = unedited_src.substr(0,matches["index"]);
+ 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";
+ }
+ unedited_src = unedited_src.substr(matches["index"],unedited_src.length);
+ // TODO: support multiline comments
+ matches_end = /\/\/\s*?(@license-end)/gm.exec(unedited_src);
+ if(matches_end == null){
+ console.log("ERROR: @license with no @license-end");
+ return [false,"\n/*\n ERROR: @license with no @license-end \n*/\n","ERROR: @license with no @license-end"];
+ }
+ 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];
+ }
+ // trim off everything we just evaluated
+ unedited_src = unedited_src.substr(endtag_end_index,unedited_src.length);
+ }
+}
-// (insert eval_test.js here)
+var counters = {};
-/* *********************************************************************************************** */
+/**
+* handles the counter for how many remote scripts are loaded
+*/
+function check_done(tabid){
+ var flag = false;
+ if(counters[tabid] === undefined){
+ // For some reason, we deleted it early
+ // Return true causing it to update the popup again
+ return true;
+ }
-function get_script(url){
+ var amt_done = counters[tabid][0];
+ var amt_remote_scripts = counters[tabid][1];
+ if(amt_done >= amt_remote_scripts ){
+ delete counters[tabid];
+ flag = true;
+ } else{
+ counters[tabid][0]++;
+ }
+ console.log(amt_done + "/" + amt_remote_scripts);
+ return flag;
+}
+/*
+* Initializes the check_done function for when arg "domain" is loading
+*/
+function setup_counter(source,tabid){
+ var amt_remote = 0;
+ matches = source.match(/(<\s*script.*?>)[\s\S]*?<\s*\/script\s*>/g);
+ for(var i = 0; i < matches.length; i++){
+ if(matches[i].match(/<.*?>/g)[0].match(/src\s*=\s*(["|']).*?(["|'])/g) != null){
+ amt_remote++;
+ }
+ }
+ counters[tabid+""] = [0,amt_remote];
+}
+/* *********************************************************************************************** */
+function get_script(url,tabid){
return new Promise((resolve, reject) => {
var response = get_content(url);
response.then(function(response) {
- var edited = "console.log('it worked');\n"+response.responseText;
- var blob = new Blob([edited], {type : 'application/javascript'});
+ var tok_index = url.split("/").length;
+ var scriptname = url.split("/")[tok_index-1];
+ // TODO: test if this script is whitelisted by name (from the GUI with the button)
+ var edited = license_read(response.responseText,scriptname);
+ // TODO: Evaluate why this counter system would fail
+ if(check_done(tabid) == true){
+ // update the popup
+ }
+ console.log("tabid:"+tabid);
+ if(unused_data[tabid] === undefined){
+ var domain = url.replace('http://','').replace('https://','').split(/[/?#]/)[0];
+ unused_data[tabid] = {"url":domain,"accepted":[],"blocked":[]};
+ }
+ if(edited[0] == true){
+ unused_data[tabid]["accepted"].push([scriptname,edited[2]]);
+ } else{
+ unused_data[tabid]["blocked"].push([scriptname,edited[2]]);
+ }
+ var blob = new Blob([edited[1]], {type : 'application/javascript'});
resolve(get_data_url(blob));
});
});
}
function read_script(a){
- return get_script(a.url);
+ function cb(whitelisted){
+ if(whitelisted == true){
+ // Doesn't matter if this is accepted or blocked, it will still be whitelisted
+ console.log('Accepted for reason "whitelisted"');
+ }
+ return get_script(a.url,a["tabId"]);
+ }
+ // TODO Change this to the script name as used in eval_test.js
+ // We can't rely on the domain in this case since scripts come from all over the place
+ var domain = a.url.replace('http://','').replace('https://','').split(/[/?#]/)[0];
+ return test_url_whitelisted(domain,cb);
}
function read_document(a){
// This needs to be handled in a different way because it sets the domain
- // of the document to data:, so it breaks all the relative URLs of imgs, styles, etc.
- return;
-
- /*
+ // of the document to "data:". This breaks relative URLs.
return new Promise((resolve, reject) => {
var response = get_content(a.url);
response.then(function(res){
- get_final_page(res.response,function(a){
- console.log("returned");
- if(typeof(a) == "boolean"){
- return;
- }
- var blob = new Blob([a.documentElement.innerHTML], {type : 'text/html'});
- resolve(get_data_url(blob));
- });
+ setup_counter(res.response,a["tabId"])
+ resolve();
+ //var blob = new Blob([res.response], {type : 'text/html'});
+ //resolve(get_data_url(blob));
});
});
- */
-
}
/**
@@ -486,13 +818,15 @@ function init_addon(){
*
* It does NOT test against the individual entries created by hitting the "whitelist"
* button for a script in the browser action.
-*
-*
-*
*/
function test_url_whitelisted(url,callback){
function storage_got(items){
- var wl = items["pref_whitelist"].split(",");
+ var wl = items["pref_whitelist"];
+ if(wl !== undefined){
+ wl = wl.split(",");
+ } else{
+ return callback(false);
+ }
var regex;
for(i in wl){
@@ -501,12 +835,12 @@ function test_url_whitelisted(url,callback){
regex = new RegExp(s, "g");
if(url.match(regex)){
//callback("%c" + wl[i] + " matched " + url,"color: purple;");
- callback(true);
+ return callback(true);
} else{
//console.log("%c" + wl[i] + " didn't match " + url,"color: #dd0000;");
}
}
- callback(false);
+ return callback(false);
}
webex.storage.local.get(storage_got);
}