/** * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript. * * * Copyright (C) 2011, 2012, 2013, 2014 Loic J. Duros * Copyright (C) 2014, 2015 Nik Nyby * * This file is part of GNU LibreJS. * * GNU LibreJS 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. * * GNU LibreJS 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 GNU LibreJS. If not, see . */ var {Cc, Ci, Cu, Cm, Cr} = require("chrome"); var observerService = Cc["@mozilla.org/observer-service;1"] .getService(Ci.nsIObserverService); var acceptedScripts = require("../script_entries/accepted_scripts") .acceptedScripts; var allowedRef = require('../http_observer/allowed_referrers') .allowedReferrers; var urlHandler = require("../url_handler/url_handler"); var ScriptAnalyzer = function() { // the URL of the current page. this.pageURL = null; }; /* * analyzeScriptBeforeExec * * Ensure that the script is found in the acceptedScript before * allowing to execute it. * */ ScriptAnalyzer.prototype.analyzeScriptBeforeExec = function (e) { if (typeof e.target.textContent === 'undefined') { throw new TypeError('event.target must be a script element'); } console.debug('analyzeScriptBeforeExec executed'); var script = e.target, isAccepted; var text = script.src ? script.src : script.textContent.substring(0,100); var notif = require("../ui/notification") .createNotification(text).notification; this.pageURL = urlHandler.removeFragment(script.ownerDocument.URL); if (!allowedRef.urlInAllowedReferrers(this.pageURL)) { if (script.src !== undefined && script.src !== '') { isAccepted = this.checkExternalScript(script); } else { isAccepted = this.checkInlineScript(script); } if (isAccepted === false && !(/^(file:\/\/|chrome:\/\/|about\:)/.test(this.pageURL)) ) { console.debug(this.pageURL); // file:// types of pages are allowed. // set invalid type so that the script is detected // by LibreJS as blocked (although it's blocked using // preventDefault(). script.setAttribute('type', 'librejs/blocked'); //script.setAttribute('data-librejs-blocked', 'dynamically'); script.setAttribute('data-librejs-blocked-src', script.src); script.removeAttribute('src'); e.preventDefault(); return false; } else { console.debug("script is accepted", script.src); script.setAttribute('data-librejs-accepted', 'dynamically'); return true; } } else { return true; } return false; }; ScriptAnalyzer.prototype.checkExternalScript = function (script) { var url = urlHandler.resolve(this.pageURL, script.src); if (this.isScriptAccepted(url, false)) { // url in src attribute is found as accepted. return true; } else { // the script hasn't been accepted before. // block it. console.debug("script is not accepted", script.src); return false; } }; ScriptAnalyzer.prototype.checkInlineScript = function (script) { return this.isScriptAccepted(script.text, true); }; ScriptAnalyzer.prototype.isScriptAccepted = function (contents, inline) { if (!acceptedScripts.isFound(this.pageURL, {'inline': inline, 'contents': contents})) { return false; } else { return true; } }; var scriptAnalyzer = new ScriptAnalyzer(); var jsLoadObserver = { observe: function (domWindow) { domWindow.document.addEventListener( "beforescriptexecute", scriptAnalyzer.analyzeScriptBeforeExec.bind(scriptAnalyzer), false); } }; observerService.addObserver(jsLoadObserver, 'content-document-global-created', false); exports.removeJsLoadObserver = function () { observerService.removeObserver(jsLoadObserver, 'content-document-global-created'); console.debug('removing jsLoadObserver'); };