aboutsummaryrefslogtreecommitdiffstats
path: root/data/script_detector/script_detector.js
diff options
context:
space:
mode:
authorNik Nyby <nikolas@gnu.org>2015-01-17 17:12:36 -0500
committerNik Nyby <nikolas@gnu.org>2015-01-17 17:12:36 -0500
commitada88090ead2c3b9d0804794c5f20f9b24d1c2b1 (patch)
tree2838a7eee6c5d74094216acebd86915e0ea1de42 /data/script_detector/script_detector.js
downloadlibrejsxul-ada88090ead2c3b9d0804794c5f20f9b24d1c2b1.tar.lz
librejsxul-ada88090ead2c3b9d0804794c5f20f9b24d1c2b1.tar.xz
librejsxul-ada88090ead2c3b9d0804794c5f20f9b24d1c2b1.zip
Import to new git repository
The old repository was using almost 100mb of space because of all the unnecessary files in the history. So I've imported the code to a new git repository. Unfortunately the history isn't viewable from this repository anymore. To see what happened with LibreJS before 2015, see the old Bazaar repo here: http://bzr.savannah.gnu.org/lh/librejs/
Diffstat (limited to 'data/script_detector/script_detector.js')
-rw-r--r--data/script_detector/script_detector.js359
1 files changed, 359 insertions, 0 deletions
diff --git a/data/script_detector/script_detector.js b/data/script_detector/script_detector.js
new file mode 100644
index 0000000..8c96cc4
--- /dev/null
+++ b/data/script_detector/script_detector.js
@@ -0,0 +1,359 @@
+/**
+ * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+ * *
+ * Copyright (C) 2011, 2012, 2014 Loic J. Duros
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// array reflects valid types as listed in
+// http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsScriptLoader.cpp#437
+// anything appended to end of strings is considered valid:
+var jsValidTypes = [
+ /^text\/javascript/i,
+ /^text\/ecmascript/i,
+ /^application\/javascript/i,
+ /^application\/ecmascript/i,
+ /^application\/x-javascript/i
+];
+
+// 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"];
+
+
+/**
+ * scriptHasInvalidType
+ *
+ * Checks that a script does not have a js "template" type.
+ * Normally any script that has a type attribute other than the
+ * few allowed ones is not interpreted. But by security, we only
+ * discard a few of them.
+ *
+ * @param script obj The script element.
+ * @return returns true if it matches a template type.
+ *
+ */
+var scriptHasInvalidType = function (type) {
+
+ var i = 0,
+ le = jsValidTypes.length;
+
+ if (type === 'librejs/blocked') {
+ // js has already been blocked.
+ return true;
+ }
+
+ if (!type) {
+ // type isn't set, don't look further.
+ return false;
+ }
+
+ for (; i < le; i++) {
+ if (jsValidTypes[i].test(type)) {
+ return false;
+ }
+ }
+
+ // type is invalid and
+ // hence cannot be executed.
+ return true;
+
+};
+
+
+/**
+ * scriptDetector
+ * Detects all scripts (inline, onpage, and external)
+ * and checks whether they have been blocked or if they
+ * are being executed.
+ */
+
+var scriptDetector = {
+ contactLink: null,
+ blockedScripts: null,
+ acceptedScripts: null,
+ dryRunScripts: null,
+ acceptedAttributes: null,
+ acceptedCode: [],
+ blockedCode: [],
+ dryRunCode: [],
+
+ init: function() {
+ if (typeof $ !== 'function') {
+ return;
+ }
+ this.blockedScripts = $('script[type="librejs/blocked"]');
+ this.acceptedScripts = $('script[type!="librejs/blocked"]')
+ .not('script[data-librejs-dryrun]');
+ this.dryRunScripts = $('script[data-librejs-dryrun]');
+ console.debug(this.dryRunScripts);
+ this.fetchAllNonScriptTags();
+
+ if (this.blockedScripts.length) {
+ // display noscript tags if applicable.
+ this.displayNoScriptTags();
+ try {
+ // initialize the page mod code.
+ pageModFinder.init();
+ } catch (e) {
+ // fail silently.
+ }
+ this.fetchBlockedScripts();
+ }
+
+ if (this.acceptedScripts.length) {
+ this.fetchAcceptedScripts();
+ }
+ if (this.dryRunScripts.length) {
+ this.fetchDryRunScripts();
+ }
+
+ self.postMessage({
+ 'event': 'scriptsFetched',
+ 'value': {
+ 'blocked': this.blockedCode,
+ 'accepted': this.acceptedCode,
+ 'dryRun': this.dryRunCode
+ }
+ });
+ },
+
+ /**
+ * fetchBlockedScripts
+ *
+ * Gather blocked scripts.
+ *
+ */
+ fetchBlockedScripts: function () {
+ var that = this,
+ singleton = '', reason;
+
+ this.blockedScripts.each(function() {
+ singleton = '';
+ reason = "";
+
+
+ if ($(this).data('librejs-reason') && $(this).data('librejs-reason').length > 9) {
+ reason = $(this).data('librejs-reason') + ': ';
+ }
+
+ if ($(this).text()) {
+
+ if ($(this).data('singleton') === true) {
+ singleton = 'This script was removed before LibreJS analysis: ';
+ }
+
+ that.blockedCode.push({'contents': singleton + reason + that.truncateText($(this).text()),
+ 'inline': true});
+ }
+
+ if ($(this).data('librejs-blocked-src')) {
+
+ that.blockedCode.push({'url': $(this).data('librejs-blocked-src'), 'contents': reason, 'inline': false});
+ }
+
+ });
+ },
+
+ /**
+ * fetchAcceptedScripts
+ *
+ * Gather accepted scripts.
+ *
+ */
+ fetchAcceptedScripts: function () {
+
+ var that = this, typeMessage = '', reason = "";
+
+ this.acceptedScripts.each(function() {
+ reason = "";
+
+ if ($(this).data('librejs-reason') && $(this).data('librejs-reason').length > 9) {
+ reason = $(this).data('librejs-reason') + ':';
+ }
+
+ if ($(this).attr('type') &&
+ scriptHasInvalidType($(this).attr('type'))) {
+ typeMessage = 'script type is not valid (js is not executed): '+ $(this).attr('type') + ' ';
+ }
+
+ if ($(this).text()) {
+ that.acceptedCode.push({'contents': reason + typeMessage + that.truncateText($(this).text()),
+ 'inline': true});
+ }
+
+ if ($(this).attr('src')) {
+ that.acceptedCode.push({'url': $(this).attr('src'), 'contents': reason + typeMessage,
+ 'inline': false});
+ }
+
+ });
+
+ },
+
+ /**
+ * fetchDryRunScripts
+ *
+ * Gather accepted scripts.
+ *
+ */
+ fetchDryRunScripts: function () {
+ var that = this, typeMessage = '', reason = "";
+ this.dryRunScripts.each(function() {
+ reason = "";
+ if ($(this).data('librejs-reason') && $(this).data('librejs-reason').length > 9) {
+ reason = $(this).data('librejs-reason') + ':';
+ }
+
+ if ($(this).attr('type') &&
+ scriptHasInvalidType($(this).attr('type'))) {
+ typeMessage = 'script type is not valid (js is not executed): '+ $(this).attr('type') + ' ';
+ }
+
+ if ($(this).text()) {
+ that.dryRunCode.push({'contents': reason + typeMessage + that.truncateText($(this).text()),
+ 'inline': true});
+ }
+
+ if ($(this).attr('src')) {
+ that.dryRunCode.push({'url': $(this).attr('src'), 'contents': reason + typeMessage,
+ 'inline': false});
+ }
+
+ });
+
+ },
+
+ fetchAllNonScriptTags: function () {
+ var that = this;
+ var blockedAnchors = $('*[data-librejs="rejected"]').not('script');
+ var acceptedAnchors = $('*[data-librejs="accepted"]').not('script');
+ var i = 0, le, attributes;
+
+ acceptedAnchors.each(function () {
+
+ var content = "";
+
+ if ($(this).attr('href')) {
+
+ content = $(this).attr('href');
+
+ }
+
+ else {
+ content = that.findOnAttributeContent($(this));
+ }
+
+ that.acceptedCode.push(
+ {contents: 'in attribute: ' + content,
+ inline: true}
+ );
+
+ });
+
+ blockedAnchors.each(function () {
+
+ var content = "";
+
+ if ($(this).attr('href')) {
+ content = $(this).attr('href');
+ } else if ($(this).data('librejs-blocked-event')) {
+ attributes = $(this).data('librejs-blocked-event');
+ le = attributes.length;
+ for (i = 0; i < le; i++) {
+ content += attributes[i].attribute + ":" + attributes[i].value + ";\n";
+ }
+ }
+
+ that.blockedCode.push(
+ {contents: 'in attribute: ' + content,
+ inline: true}
+ );
+ });
+ },
+
+ findOnAttributeContent: function (elem) {
+ var i = 0,
+ le = intrinsicEvents.length,
+ content = "";
+
+ for (; i < le; i++) {
+
+ if (elem.attr(intrinsicEvents[i])) {
+
+ content += elem.attr(intrinsicEvents[i]) + " -- ";
+
+ }
+
+ }
+ return content;
+ },
+
+ /**
+ * displayNoScriptTags
+ * Whenever blocked scripts are found, deep clone noscript tags
+ * and place them in a new div.
+ */
+ displayNoScriptTags: function () {
+
+ var noscripts = $('body noscript'),
+ div, content;
+
+ noscripts.each(function (index) {
+ div = $('<div/>');
+
+
+ content = $(this).contents();
+ content = $("<div>").html(content).text();
+
+ div.append(content);
+ div.children('style, script, meta').remove();
+
+
+ // insert noscript content right after the
+ // original noscript tag.
+ div.insertAfter($(this));
+ });
+
+ },
+ truncateText: function (str) {
+
+ if (str.length > 1000) {
+ str = str.slice(0, 1000) + '…';
+ }
+ return str;
+ }
+
+};
+
+scriptDetector.init();