summaryrefslogtreecommitdiff
path: root/background/ResponseHandler.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'background/ResponseHandler.mjs')
-rw-r--r--background/ResponseHandler.mjs257
1 files changed, 0 insertions, 257 deletions
diff --git a/background/ResponseHandler.mjs b/background/ResponseHandler.mjs
deleted file mode 100644
index 6b979e6..0000000
--- a/background/ResponseHandler.mjs
+++ /dev/null
@@ -1,257 +0,0 @@
-/**
-* GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
-* *
-* Copyright (C) 2017, 2018 Nathan Nichols
-* Copyright (C) 2018 Ruben Rodriguez <ruben@gnu.org>
-*
-* 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 <http://www.gnu.org/licenses/>.
-*/
-
-/**
-* This listener gets called as soon as we've got all the HTTP headers, can guess
-* content type and encoding, and therefore correctly parse HTML documents
-* and external script inclusions in search of crappy JavaScript
-*/
-
-import inject_scripts from './script_injector.mjs';
-import {ResponseProcessor} from './ResponseProcessor.mjs';
-
-"use strict";
-
-var ResponseHandler = {
- /**
- * Enforce white/black lists for url/site early (hashes will be handled later)
- */
- async pre(response) {
- // TODO: reimplement blacklisting/whitelisting later
- if (true) return ResponseProcessor.CONTINUE;
-
- let {request} = response;
- let {url, type, tabId, frameId, documentUrl} = request;
-
- let fullUrl = url;
- url = ListStore.urlItem(url);
- let site = ListStore.siteItem(url);
-
- let blacklistedSite = ListManager.siteMatch(site, blacklist);
- let blacklisted = blacklistedSite || blacklist.contains(url);
- let topUrl = type === "sub_frame" && request.frameAncestors && request.frameAncestors.pop() || documentUrl;
-
- if (blacklisted) {
- if (type === "script") {
- // this shouldn't happen, because we intercept earlier in blockBlacklistedScripts()
- return ResponseProcessor.REJECT;
- }
- if (type === "main_frame") { // we handle the page change here too, since we won't call edit_html()
- activityReports[tabId] = await createReport({url: fullUrl, tabId});
- // Go on without parsing the page: it was explicitly blacklisted
- let reason = blacklistedSite
- ? `All ${blacklistedSite} blacklisted by user`
- : "Address blacklisted by user";
- await addReportEntry(tabId, url, {"blacklisted": [blacklistedSite || url, reason], url: fullUrl});
- }
- // use CSP to restrict JavaScript execution in the page
- request.responseHeaders.unshift({
- name: `Content-security-policy`,
- value: `script-src 'none';`
- });
- return {responseHeaders: request.responseHeaders}; // let's skip the inline script parsing, since we block by CSP
- } else {
-
- let whitelistedSite = ListManager.siteMatch(site, whitelist);
- let whitelisted = response.whitelisted = whitelistedSite || whitelist.contains(url);
- if (type === "script") {
- if (whitelisted) {
- // accept the script and stop processing
- addReportEntry(tabId, url, {url: topUrl,
- "whitelisted": [url, whitelistedSite ? `User whitelisted ${whitelistedSite}` : "Whitelisted by user"]});
- return ResponseProcessor.ACCEPT;
- } else {
- let scriptInfo = await ExternalLicenses.check({url: fullUrl, tabId, frameId, documentUrl});
- if (scriptInfo) {
- let verdict, ret;
- let msg = scriptInfo.toString();
- if (scriptInfo.free) {
- verdict = "accepted";
- ret = ResponseProcessor.ACCEPT;
- } else {
- verdict = "blocked";
- ret = ResponseProcessor.REJECT;
- }
- addReportEntry(tabId, url, {url, [verdict]: [url, msg]});
- return ret;
- }
- }
- }
- }
- // it's a page (it's too early to report) or an unknown script:
- // let's keep processing
- return ResponseProcessor.CONTINUE;
- },
-
- /**
- * Here we do the heavylifting, analyzing unknown scripts
- */
- async post(response) {
- let {type} = response.request;
- return await handle_html(response, response.whitelisted);
- }
-}
-
-/**
-* Serializes HTMLDocument objects including the root element and
-* the DOCTYPE declaration
-*/
-function doc2HTML(doc) {
- let s = doc.documentElement.outerHTML;
- if (doc.doctype) {
- let dt = doc.doctype;
- let sDoctype = `<!DOCTYPE ${dt.name || "html"}`;
- if (dt.publicId) sDoctype += ` PUBLIC "${dt.publicId}"`;
- if (dt.systemId) sDoctype += ` "${dt.systemId}"`;
- s = `${sDoctype}>\n${s}`;
- }
- return s;
-}
-
-/**
-* Shortcut to create a correctly namespaced DOM HTML elements
-*/
-function createHTMLElement(doc, name) {
- return doc.createElementNS("http://www.w3.org/1999/xhtml", name);
-}
-
-/**
-* Replace any element with a span having the same content (useful to force
-* NOSCRIPT elements to visible the same way as NoScript and uBlock do)
-*/
-function forceElement(doc, element) {
- let replacement = createHTMLElement(doc, "span");
- replacement.innerHTML = element.innerHTML;
- element.replaceWith(replacement);
- return replacement;
-}
-
-/**
-* Forces displaying any element having the "data-librejs-display" attribute and
-* <noscript> elements on pages where LibreJS disabled inline scripts (unless
-* they have the "data-librejs-nodisplay" attribute).
-*/
-function forceNoscriptElements(doc) {
- let shown = 0;
- // inspired by NoScript's onScriptDisabled.js
- for (let noscript of doc.querySelectorAll("noscript:not([data-librejs-nodisplay])")) {
- let replacement = forceElement(doc, noscript);
- // emulate meta-refresh
- let meta = replacement.querySelector('meta[http-equiv="refresh"]');
- if (meta) {
- refresh = true;
- doc.head.appendChild(meta);
- }
- shown++;
- }
- return shown;
-}
-
-/**
-* Forces displaying any element having the "data-librejs-display" attribute and
-* <noscript> elements on pages where LibreJS disabled inline scripts (unless
-* they have the "data-librejs-nodisplay" attribute).
-*/
-function showConditionalElements(doc) {
- let shown = 0;
- for (let element of document.querySelectorAll("[data-librejs-display]")) {
- forceElement(doc, element);
- shown++;
- }
- return shown;
-}
-
-/**
-
-* Reads/changes the HTML of a page and the scripts within it.
-*/
-async function editHtml(html, documentUrl, tabId, frameId, whitelisted){
-
- var parser = new DOMParser();
- var html_doc = parser.parseFromString(html, "text/html");
-
- if (whitelisted) { // don't bother rewriting
- return null;
- }
-
- var scripts = html_doc.scripts;
-
- let findLine = finder => finder.test(html) && html.substring(0, finder.lastIndex).split(/\n/).length || 0;
-
- let modified = false;
- // Deal with intrinsic events
- let intrinsicFinder = /<[a-z][^>]*\b(on\w+|href\s*=\s*['"]?javascript:)/gi;
- for (let element of html_doc.all) {
- let line = -1;
- for (let attr of element.attributes) {
- let {name, value} = attr;
- value = value.trim();
- if (name.startsWith("on")) {
- attr.value = "console.log(\"event script blocked by myext\")";
- } else if (name === "href" && value.toLowerCase().startsWith("javascript:")){
- if (line === -1) {
- line = findLine(intrinsicFinder);
- }
- try {
- attr.value = `view-source:${documentUrl}#line${line}`;
- } catch (e) {
- console.error(e);
- }
- }
- }
- }
-
- let modifiedInline = false;
- let scriptFinder = /<script\b/ig;
- for(let i = 0, len = scripts.length; i < len; i++) {
- let script = scripts[i];
- let line = findLine(scriptFinder);
- if (!script.src) {
- script.textContent = `//script blocked, you can examine it at view-source:${documentUrl}#line${line}`;
- } else {
- let src = script.src;
- script.removeAttribute("src");
- script.setAttribute("blocked-src", src);
- script.textContent = "//script blocked";
- }
- }
-
- showConditionalElements(html_doc);
- forceNoscriptElements(html_doc);
- await inject_scripts(documentUrl, html_doc);
- return doc2HTML(html_doc);
-}
-
-/**
-* Here we handle html document responses
-*/
-async function handle_html(response, whitelisted) {
- let {text, request} = response;
- let {url, tabId, frameId, type} = request;
- if (type === "main_frame") {
- //activityReports[tabId] = await createReport({url, tabId});
- //updateBadge(tabId);
- }
- return await editHtml(text, url, tabId, frameId, whitelisted);
-}
-
-export default ResponseHandler;