[nscl] More refactoring out in NoScript Commons Library.

This commit is contained in:
hackademix 2021-03-20 18:04:25 +01:00
parent 4c9ddc847a
commit c30c9c5627
34 changed files with 65 additions and 2962 deletions

View File

@ -229,7 +229,7 @@ var LifeCycle = (() => {
if (!previousVersion) return;
await include("/lib/Ver.js");
await include("/nscl/common/Ver.js");
previousVersion = new Ver(previousVersion);
let currentVersion = new Ver(browser.runtime.getManifest().version);
let upgrading = Ver.is(previousVersion, "<=", currentVersion);
@ -263,7 +263,7 @@ var LifeCycle = (() => {
// user doesn't want us to remember temporary settings across updates: bail out
return;
}
await include("/lib/Ver.js");
await include("/nscl/common/Ver.js");
if (Ver.is(details.version, "<", browser.runtime.getManifest().version)) {
// downgrade: temporary survival might not be supported, and we don't care
return;

View File

@ -141,7 +141,7 @@ var Settings = {
? Object.assign(ns[storage], settings[storage]) : ns[storage] = Object.assign({}, ns.defaults[storage]))
));
if (ns.local.debug !== oldDebug) {
await include("/lib/log.js");
await include("/nscl/common/log.js");
if (oldDebug) debug = () => {};
}

View File

@ -310,7 +310,7 @@
debug("Collected seen", seen);
return seen;
} catch (e) {
await include("/lib/restricted.js");
await include("/nscl/common/restricted.js");
if (!isRestrictedURL((await browser.tabs.get(tabId)).url)) {
// probably a page where content scripts cannot run, let's open the options instead
error(e, "Cannot collect noscript activity data");

View File

@ -1,33 +0,0 @@
"use strict";
function CapsCSP(baseCSP = new CSP()) {
return Object.assign(baseCSP, {
types: ["script", "object", "media", "font"],
dataUriTypes: ["font", "media", "object"],
buildFromCapabilities(capabilities, blockHttp = false) {
let forbidData = new Set(this.dataUriTypes.filter(t => !capabilities.has(t)));
let blockedTypes = new Set(this.types.filter(t => !capabilities.has(t)));
if(!capabilities.has("script")) {
blockedTypes.add({name: "script-src-elem"});
blockedTypes.add({name: "script-src-attr"});
blockedTypes.add("worker");
if (!blockedTypes.has("object")) {
// data: URIs loaded in objects may run scripts
blockedTypes.add({type: "object", value: "http:"});
}
}
if (!blockHttp) {
// HTTP is blocked in onBeforeRequest, let's allow it only and block
// for instance data: and blob: URIs
for (let type of this.dataUriTypes) {
if (blockedTypes.delete(type)) {
blockedTypes.add({type, value: "http:"});
}
}
}
return blockedTypes.size ? this.buildBlocker(...blockedTypes) : null;
}
});
}

View File

@ -1,169 +0,0 @@
"use strict";
var Storage = (() => {
let chunksKey = k => `${k}/CHUNKS`;
async function safeOp(op, type, keys) {
let sync = type === "sync";
try {
if (sync) {
let remove = op === "remove";
if (remove || op === "get") {
keys = [].concat(keys); // don't touch the passed argument
let mergeResults = {};
let localFallback = await getLocalFallback();
if (localFallback.size) {
let localKeys = keys.filter(k => localFallback.has(k));
if (localKeys.length) {
if (remove) {
await browser.storage.local.remove(localKeys);
for (let k of localKeys) {
localFallback.delete(k);
}
await setLocalFallback(localFallback);
} else {
mergeResults = await browser.storage.local.get(localKeys);
}
keys = keys.filter(k => !localFallback.has(k));
}
}
if (keys.length) { // we may not have non-fallback keys anymore
let chunkCounts = Object.entries(await browser.storage.sync.get(
keys.map(chunksKey)))
.map(([k, count]) => [k.split("/")[0], count]);
if (chunkCounts.length) {
let chunkedKeys = [];
for (let [k, count] of chunkCounts) {
// prepare to fetch all the chunks at once
while (count-- > 0) chunkedKeys.push(`${k}/${count}`);
}
if (remove) {
let doomedKeys = keys
.concat(chunkCounts.map(([k, count]) => chunksKey(k)))
.concat(chunkedKeys);
return await browser.storage.sync.remove(doomedKeys);
} else {
let chunks = await browser.storage.sync.get(chunkedKeys);
for (let [k, count] of chunkCounts) {
let orderedChunks = [];
for (let j = 0; j < count; j++) {
orderedChunks.push(chunks[`${k}/${j}`]);
}
let whole = orderedChunks.join('');
try {
mergeResults[k] = JSON.parse(whole);
keys.splice(keys.indexOf(k), 1); // remove from "main" keys
} catch (e) {
error(e, "Could not parse chunked storage key %s (%s).", k, whole);
}
}
}
}
}
return keys.length ?
Object.assign(mergeResults, await browser.storage.sync[op](keys))
: mergeResults;
} else if (op === "set") {
keys = Object.assign({}, keys); // don't touch the passed argument
const MAX_ITEM_SIZE = 4096;
// Firefox Sync's max object BYTEs size is 16384, Chrome's 8192.
// Rather than mesuring actual bytes, we play it safe by halving then
// lowest to cope with escapes / multibyte characters.
let removeKeys = [];
for (let k of Object.keys(keys)) {
let s = JSON.stringify(keys[k]);
let chunksCountKey = chunksKey(k);
let oldCount = await browser.storage.sync.get(chunksCountKey)[chunksCountKey] || 0;
let count;
if (s.length > MAX_ITEM_SIZE) {
count = Math.ceil(s.length / MAX_ITEM_SIZE);
let chunks = {
[chunksCountKey]: count
};
for(let j = 0, o = 0; j < count; ++j, o += MAX_ITEM_SIZE) {
chunks[`${k}/${j}`] = s.substr(o, MAX_ITEM_SIZE);
}
await browser.storage.sync.set(chunks);
keys[k] = "[CHUNKED]";
} else {
count = 0;
removeKeys.push(chunksCountKey);
}
if (oldCount-- > count) {
do {
removeKeys.push(`${k}${oldCount}`);
} while(oldCount-- > count);
}
}
await browser.storage.sync.remove(removeKeys);
}
}
let ret = await browser.storage[type][op](keys);
if (sync && op === "set") {
let localFallback = await getLocalFallback();
let size = localFallback.size;
if (size > 0) {
for (let k of Object.keys(keys)) {
localFallback.delete(k);
}
if (size > localFallback.size) {
await setLocalFallback(localFallback);
}
}
}
return ret;
} catch (e) {
error(e, "%s.%s(%o)", type, op, keys);
if (sync) {
debug("Sync disabled? Falling back to local storage (%s %o)", op, keys);
let localFallback = await getLocalFallback();
let failedKeys = Array.isArray(keys) ? keys
: typeof keys === "string" ? [keys] : Object.keys(keys);
for (let k of failedKeys) {
localFallback.add(k);
}
await setLocalFallback(localFallback);
} else {
throw e;
}
}
return await browser.storage.local[op](keys);
}
const LFK_NAME = "__fallbackKeys";
async function setLocalFallback(keys) {
return await browser.storage.local.set({[LFK_NAME]: [...keys]});
}
async function getLocalFallback() {
let keys = (await browser.storage.local.get(LFK_NAME))[LFK_NAME];
return new Set(Array.isArray(keys) ? keys : []);
}
return {
async get(type, keys) {
return await safeOp("get", type, keys);
},
async set(type, keys) {
return await safeOp("set", type, keys);
},
async remove(type, keys) {
return await safeOp("remove", type, keys);
},
async hasLocalFallback(key) {
return (await getLocalFallback()).has(key);
},
async isChunked(key) {
let ccKey = chunksKey(key);
let data = await browser.storage.sync.get([key, ccKey]);
return data[key] === "[CHUNKED]" && parseInt(data[ccKey]);
}
};
})()

View File

@ -1,29 +0,0 @@
class SyntaxChecker {
constructor() {
this.lastError = null;
this.lastFunction = null;
this.lastScript = "";
}
check(script) {
this.lastScript = script;
try {
return !!(this.lastFunction = new Function(script));
} catch(e) {
this.lastError = e;
this.lastFunction = null;
}
return false;
}
unquote(s, q) {
// check that this is really a double or a single quoted string...
if (s.length > 1 && s.startsWith(q) && s.endsWith(q) &&
// if nothing is left if you remove all he escapes and all the stuff between quotes
s.replace(/\\./g, '').replace(/^(['"])[^\n\r]*?\1/, '') === '') {
try {
return eval(s);
} catch (e) {
}
}
return null;
}
}

View File

@ -1,45 +0,0 @@
'use strict';
var _ = browser.i18n.getMessage;
var i18n = (() => {
var i18n = {
// derived from http://github.com/piroor/webextensions-lib-l10n
updateString(aString) {
return aString.replace(/__MSG_(.+?)__/g, function(aMatched) {
var key = aMatched.slice(6, -2);
return _(key);
});
},
updateDOM(rootNode = document) {
var texts = document.evaluate(
'descendant::text()[contains(self::text(), "__MSG_")]',
rootNode,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
for (let i = 0, maxi = texts.snapshotLength; i < maxi; i++)
{
let text = texts.snapshotItem(i);
text.nodeValue = this.updateString(text.nodeValue);
}
var attributes = document.evaluate(
'descendant::*/attribute::*[contains(., "__MSG_")]',
rootNode,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
for (let i = 0, maxi = attributes.snapshotLength; i < maxi; i++)
{
let attribute = attributes.snapshotItem(i);
debug('apply', attribute);
attribute.value = this.updateString(attribute.value);
}
}
};
document.addEventListener('DOMContentLoaded', e => i18n.updateDOM());
return i18n;
})()

View File

@ -1,69 +0,0 @@
'use strict';
class DocumentCSP {
constructor(document) {
this.document = document;
this.builder = new CapsCSP();
}
apply(capabilities, embedding = CSP.isEmbedType(this.document.contentType)) {
let {document} = this;
if (!capabilities.has("script")) {
// safety net for XML (especially SVG) documents and synchronous scripts running
// while inserting the CSP <meta> element.
document.defaultView.addEventListener("beforescriptexecute", e => {
if (!e.isTrusted) return;
e.preventDefault();
debug("Fallback beforexecutescript listener blocked ", e.target);
}, true);
}
let csp = this.builder;
let blocker = csp.buildFromCapabilities(capabilities, embedding);
if (!blocker) return null;
let createHTMLElement =
tagName => document.createElementNS("http://www.w3.org/1999/xhtml", tagName);
let header = csp.asHeader(blocker);
let meta = createHTMLElement("meta");
meta.setAttribute("http-equiv", header.name);
meta.setAttribute("content", header.value);
let root = document.documentElement;
try {
if (!(document instanceof HTMLDocument)) {
if (!(document instanceof XMLDocument)) {
return null; // nothing to do with ImageDocument, for instance
}
// non-HTML XML documents ignore <meta> CSP unless wrapped in
// - <html><head></head></head> on Gecko
// - just <head></head> on Chromium
console.debug("XML Document: temporary replacing %o with <HTML>", root);
let htmlDoc = document.implementation.createHTMLDocument();
let htmlRoot = document.importNode(htmlDoc.documentElement, true);
document.replaceChild(htmlRoot, root);
}
let {head} = document;
let parent = head ||
document.documentElement.insertBefore(createHTMLElement("head"),
document.documentElement.firstElementChild);
parent.insertBefore(meta, parent.firstElementChild);
debug(`Failsafe <meta> CSP inserted in %s: "%s"`, document.URL, header.value);
meta.remove();
if (!head) parent.remove();
if (document.documentElement !== root)
{
document.replaceChild(root, document.documentElement);
}
} catch (e) {
error(e, "Error inserting CSP %s in %s", document.URL, header && header.value);
return null;
}
return CSP.normalize(header.value);
}
}

View File

@ -1,225 +0,0 @@
var PlaceHolder = (() => {
const HANDLERS = new Map();
const CLASS_NAME = "__NoScript_PlaceHolder__";
const SELECTOR = `a.${CLASS_NAME}`;
let checkStyle = async () => {
checkStyle = () => {};
if (!ns.embeddingDocument) return;
let replacement = document.querySelector(SELECTOR);
if (!replacement) return;
if (window.getComputedStyle(replacement, null).opacity !== "0.8") {
document.head.appendChild(createHTMLElement("style")).textContent = await
(await fetch(browser.extension.getURL("/content/content.css"))).text();
}
}
class Handler {
constructor(type, selector) {
this.type = type;
this.selector = selector;
this.placeHolders = new Map();
HANDLERS.set(type, this);
}
filter(element, request) {
if (request.embeddingDocument) {
return document.URL === request.url;
}
let url = request.initialUrl || request.url;
return "data" in element ? element.data === url : element.src === url;
}
selectFor(request) {
return [...document.querySelectorAll(this.selector)]
.filter(element => this.filter(element, request))
}
}
new Handler("frame", "iframe");
new Handler("object", "object, embed");
new Handler("media", "video, audio, source");
function cloneStyle(src, dest,
props = ["width", "height", "position", "*", "margin*"]) {
var suffixes = ["Top", "Right", "Bottom", "Left"];
for (let i = props.length; i-- > 0;) {
let p = props[i];
if (p.endsWith("*")) {
let prefix = p.substring(0, p.length - 1);
props.splice(i, 1, ...
(suffixes.map(prefix ? (suffix => prefix + suffix) :
suffix => suffix.toLowerCase())));
}
};
let srcStyle = window.getComputedStyle(src, null);
let destStyle = dest.style;
for (let p of props) {
destStyle[p] = srcStyle[p];
}
for (let size of ["width", "height"]) {
if (/^0(?:\D|$)/.test(destStyle[size])) {
destStyle[size] = "";
}
}
if (src.offsetTop < 0 && src.offsetTop <= (-src.offsetHeight)) {
destStyle.top = "0"; // fixes video player off-display position on Youtube
}
destStyle.display = srcStyle.display !== "block" ? "inline-block" : "block";
}
class PlaceHolder {
static create(policyType, request) {
return new PlaceHolder(policyType, request);
}
static canReplace(policyType) {
return HANDLERS.has(policyType);
}
static handlerFor(policyType) {
return HANDLERS.get(policyType);
}
static listen() {
PlaceHolder.listen = () => {};
window.addEventListener("click", ev => {
if (ev.button === 0 && ev.isTrusted) {
let ph, replacement;
for (let e of document.elementsFromPoint(ev.clientX, ev.clientY)) {
if (ph = e._placeHolderObj) {
replacement = e;
break;
}
if (replacement = e._placeHolderReplacement) {
ph = replacement._placeHolderObj;
break;
}
}
if (ph) {
ev.preventDefault();
ev.stopPropagation();
if (ev.target.value === "close") {
ph.close(replacement);
} else {
ph.enable(replacement);
}
}
}
}, true, false);
}
constructor(policyType, request) {
this.policyType = policyType;
this.request = request;
this.replacements = new Set();
this.handler = PlaceHolder.handlerFor(policyType);
if (this.handler) {
[...document.querySelectorAll(this.handler.selector)]
.filter(element => this.handler.filter(element, request))
.forEach(element => this.replace(element));
};
if (this.replacements.size) {
PlaceHolder.listen();
checkStyle();
}
}
replace(element) {
if (!element.parentElement) return;
if (element.parentElement instanceof HTMLMediaElement) {
this.replace(element.parentElement);
return;
}
let {
url
} = this.request;
let objUrl = new URL(url)
this.origin = objUrl.origin;
if (this.origin === "null") {
this.origin = objUrl.protocol;
}
let TYPE = `<${this.policyType.toUpperCase()}>`;
let replacement = createHTMLElement("a");
replacement.className = CLASS_NAME;
cloneStyle(element, replacement);
if (ns.embeddingDocument) {
replacement.classList.add("__ns__document");
window.stop();
}
replacement.href = url;
replacement.title = `${TYPE}@${url}`;
let inner = replacement.appendChild(createHTMLElement("span"));
inner.className = replacement.className;
let button = inner.appendChild(createHTMLElement("button"));
button.className = replacement.className;
button.setAttribute("aria-label", button.title = _("Close"));
button.value = "close";
button.textContent = "×";
let description = inner.appendChild(createHTMLElement("span"));
description.textContent = `${TYPE}@${this.origin}`;
replacement._placeHolderObj = this;
replacement._placeHolderElement = element;
for (let e of replacement.querySelectorAll("*")) {
e._placeHolderReplacement = replacement;
}
element.replaceWith(replacement);
// do our best to bring it to front
for (let p = replacement; p = p.parentElement;) {
p.classList.add("__ns__pop2top");
};
this.replacements.add(replacement);
}
async enable(replacement) {
debug("Enabling %o", this.request, this.policyType);
let ret = await Messages.send("blockedObjects", {
url: this.request.url,
policyType: this.policyType,
documentUrl: document.URL
});
debug("Received response", ret);
if (!ret) return;
// bring back ancestors
for (let p = replacement; p = p.parentElement;) {
p.classList.remove("__ns__pop2top");
};
if (ret.collapse) {
for (let collapsing of (ret.collapse === "all" ? document.querySelectorAll(SELECTOR) : [replacement])) {
this.replacements.delete(collapsing);
collapsing.remove();
}
return;
}
if (this.request.embeddingDocument) {
window.location.reload();
return;
}
try {
let element = replacement._placeHolderElement;
replacement.replaceWith(element.cloneNode(true));
this.replacements.delete(replacement);
} catch (e) {
error(e, "While replacing");
}
}
close(replacement) {
replacement.classList.add("__ns__closing");
this.replacements.delete(replacement);
window.setTimeout(() => {
for (let p = replacement; p = p.parentElement;) {
p.classList.remove("__ns__pop2top");
};
replacement.remove()
}, 500);
}
}
return PlaceHolder;
})();

View File

@ -1,106 +0,0 @@
if ("MediaSource" in window) {
let mediaBlocker;
let notify = allowed => {
let request = {
id: "noscript-media",
type: "media",
url: document.URL,
documentUrl: document.URL,
embeddingDocument: true,
};
seen.record({policyType: "media", request, allowed});
debug("MSE notification", document.URL); // DEV_ONLY
notifyPage();
return request;
};
let createPlaceholder = (mediaElement, request) => {
try {
let ph = PlaceHolder.create("media", request);
ph.replace(mediaElement);
PlaceHolder.listen();
debug("MSE placeholder for %o", mediaElement); // DEV_ONLY
} catch (e) {
error(e);
}
};
if ("SecurityPolicyViolationEvent" in window) {
// "Modern" browsers
let createPlaceholders = () => {
let request = notify(false);
for (let me of document.querySelectorAll("video,audio")) {
if (!(me.src || me.currentSrc) || me.src.startsWith("blob")) {
createPlaceholder(me, request);
}
}
}
let processedURIs = new Set();
addEventListener("securitypolicyviolation", e => {
let {blockedURI, violatedDirective, originalPolicy} = e;
if (!(e.isTrusted && violatedDirective === "media-src" && CSP.isMediaBlocker(originalPolicy))) return;
if (mediaBlocker === undefined && /^data\b/.test(blockedURI)) { // Firefox 81 reports just "data"
debug("mediaBlocker set via CSP listener.")
mediaBlocker = true;
e.stopImmediatePropagation();
return;
}
if (blockedURI.startsWith("blob") &&
!processedURIs.has(blockedURI)) {
processedURIs.add(blockedURI);
setTimeout(createPlaceholders, 0);
}
}, true);
}
if (typeof exportFunction === "function") {
// Fallback: Mozilla does not seem to trigger CSP media-src http: for blob: URIs assigned in MSE
window.wrappedJSObject.document.createElement("video").src = "data:"; // triggers early mediaBlocker initialization via CSP
ns.on("capabilities", e => {
mediaBlocker = !ns.allows("media");
if (mediaBlocker) debug("mediaBlocker set via fetched policy.")
});
let unpatched = new Map();
function patch(obj, methodName, replacement) {
let methods = unpatched.get(obj) || {};
methods[methodName] = obj[methodName];
exportFunction(replacement, obj, {defineAs: methodName});
unpatched.set(obj, methods);
}
let urlMap = new WeakMap();
patch(window.URL, "createObjectURL", function(o, ...args) {
let url = unpatched.get(window.URL).createObjectURL.call(this, o, ...args);
if (o instanceof MediaSource) {
let urls = urlMap.get(o);
if (!urls) urlMap.set(o, urls = new Set());
urls.add(url);
}
return url;
});
patch(window.MediaSource.prototype, "addSourceBuffer", function(mime, ...args) {
let ms = this;
let urls = urlMap.get(ms);
let request = notify(!mediaBlocker);
if (mediaBlocker) {
let exposedMime = `${mime} (MSE)`;
setTimeout(() => {
try {
let allMedia = [...document.querySelectorAll("video,audio")];
let me = allMedia.find(e => e.srcObject === ms ||
urls && (urls.has(e.currentSrc) || urls.has(e.src))) ||
// throwing may cause src not to be assigned at all:
allMedia.find(e => !(e.src || e.currentSrc || e.srcObject));
if (me) createPlaceholder(me, request);
} catch (e) {
error(e);
}
}, 0);
let msg = `${exposedMime} blocked by NoScript`;
log(msg);
throw new Error(msg);
}
return unpatched.get(window.MediaSource.prototype).addSourceBuffer.call(ms, mime, ...args);
});
}
}

View File

@ -1,63 +0,0 @@
'use strict';
{
let urlAttributes = ['href', 'to', 'from', 'by', 'values'];
let selector = urlAttributes.map(a => `[${a}]`).join(',');
for (let evType of ["drop", "paste"]) window.addEventListener(evType, e => {
let container = e.target;
let editing = false;
for (let el = container; el; el = el.parentElement) {
if (el.setRangeText || el.contentEditable) {
editing = true;
break;
}
}
if (!editing) return;
// we won't touch DOM elements which are already there
let oldNodes = new Set(container.querySelectorAll(selector + ",form"));
window.setTimeout(() => {
// we delay our custom sanitization after the browser performed the paste
// or drop job, rather than replacing it, in order to avoid interferences
// with built-in sanitization
try {
let html = container.innerHTML;
if (sanitizeExtras(container, oldNodes)) {
let t = e.type.toUpperCase();
console.log(`[NoScript] Sanitized\n<${t}>\n${html}\n</${t}>\nto\n<${t}>\n${container.innerHTML}\n</${t}>`, container);
}
} catch(ex) {
console.log(ex);
}
}, 0);
}, true);
function removeAttribute(node, name, value = node.getAttribute(name)) {
node.setAttribute(`data-noscript-removed-${name}`, value);
node.removeAttribute(name);
}
function sanitizeExtras(container, oldNodes = []) {
let ret = false;
// remove attributes from forms
for (let f of container.getElementsByTagName("form")) {
if (oldNodes.has(f)) continue;
for (let a of [...f.attributes]) {
removeAttribute(f, a.name);
}
}
for (let node of container.querySelectorAll(selector)) {
if (oldNodes.has(node)) continue;
for (let name of urlAttributes) {
let value = node.getAttribute(name);
if (/^\W*(?:(?:javascript|data):|https?:[\s\S]+[[(<])/i.test(unescape(value))) {
removeAttribute(node, name, value);
ret = true;
}
}
}
return ret;
}
}

View File

@ -1,43 +0,0 @@
// depends on nscl/content/patchWindow.js
"use strict";
ns.on("capabilities", event => {
debug("WebGL Hook", document.URL, document.documentElement && document.documentElement.innerHTML, ns.capabilities); // DEV_ONLY
if (ns.allows("webgl")) return;
let env = {eventName: `nsWebgl:${uuid()}`};
window.addEventListener(env.eventName, e => {
let request = {
id: "noscript-webgl",
type: "webgl",
url: document.URL,
documentUrl: document.URL,
embeddingDocument: true,
};
seen.record({policyType: "webgl", request, allowed: false});
let canvas = e.target;
if (canvas instanceof HTMLCanvasElement) {
try {
let ph = PlaceHolder.create("webgl", request);
ph.replace(canvas);
PlaceHolder.listen();
} catch (e) {
error(e);
}
}
notifyPage();
}, true);
function modifyGetContext(win, env) {
let proto = win.HTMLCanvasElement.prototype;
let getContext = proto.getContext;
exportFunction(function(type, ...rest) {
if (type && type.toLowerCase().includes("webgl")) {
let target = document.contains(this) ? this : window;
target.dispatchEvent(new Event(env.eventName, {composed: true}));
return null;
}
return getContext.call(this, type, ...rest);
}, proto, {defineAs: "getContext"});
}
patchWindow(modifyGetContext, env);
});

View File

@ -1,44 +0,0 @@
"use strict";
class CSP {
static isMediaBlocker(csp) {
return /(?:^|[\s;])media-src (?:'none'|http:)(?:;|$)/.test(csp);
}
static normalize(csp) {
return csp.replace(/\s*;\s*/g, ';').replace(/\b(script-src\s+'none'.*?;)(?:script-src-\w+\s+'none';)+/, '$1');
}
build(...directives) {
return directives.join(';');
}
buildBlocker(...types) {
return this.build(...(types.map(t => `${t.name || `${t.type || t}-src`} ${t.value || "'none'"}`)));
}
blocks(header, type) {
return `;${header};`.includes(`;${type}-src 'none';`)
}
asHeader(value) {
return {name: CSP.headerName, value};
}
}
CSP.isEmbedType = type => /\b(?:application|video|audio)\b/.test(type) && type !== "application/xhtml+xml";
CSP.headerName = "content-security-policy";
CSP.patchDataURI = (uri, blocker) => {
let parts = /^data:(?:[^,;]*ml|unknown-content-type)(;[^,]*)?,/i.exec(uri);
if (!(blocker && parts)) {
// not an interesting data: URI, return as it is
return uri;
}
if (parts[1]) {
// extra encoding info, let's bailout (better safe than sorry)
return "data:";
}
// It's a HTML/XML page, let's prepend our CSP blocker to the document
let patch = parts[0] + encodeURIComponent(
`<meta http-equiv="${CSP.headerName}" content="${blocker}"/>`);
return uri.startsWith(patch) ? uri : patch + uri.substring(parts[0].length);
}

View File

@ -1,49 +0,0 @@
/**
* Wrapper around listeners on various WebExtensions
* APIs (e.g. webRequest.on*), as a best effort to
* let them run last by removing and re-adding them
* on each call (swapping 2 copies because
* addListener() calls are asynchronous).
* Note: we rely on implementation details Like
* listeners being called in addition order; also,
* clients should ensure they're not called twice for
* the same event, if that's important.
}
*/
class LastListener {
constructor(observed, listener, ...extras) {
this.observed = observed;
this.listener = listener;
this.extras = extras;
let ww = this._wrapped = [listener, listener].map(l => {
let w = (...args) => {
if (this.observed.hasListener(w._other)) {
this.observed.removeListener(w);
} else if (this.installed) {
this.observed.addListener(w._other, ...this.extras);
}
debug("Running listener", w === ww[0] ? 0 : 1, ...args);
return this.installed ? this.listener(...args)
: this.defaultResult;
}
return w;
});
ww[0]._other = ww[1];
ww[1]._other = ww[0];
this.installed = false;
this.defaultResult = null;
}
install() {
if (this.installed) return;
this.observed.addListener(this._wrapped[0], ...this.extras);
this.installed = true;
}
uninstall() {
this.installed = false;
for (let l of this._wrapped) this.observed.removeListener(l);
}
}

View File

@ -1,27 +0,0 @@
"use strict";
class NetCSP extends CSP {
constructor(start) {
super();
this.start = start;
}
isMine(header) {
let {name, value} = header;
return name.toLowerCase() === CSP.headerName &&
value.split(/,\s*/).some(v => v.startsWith(this.start));
}
unmergeExtras(header) {
let {name, value} = header;
return value.split(/,\s*/).filter(v => !v.startsWith(this.start))
.map(value => {name, value});
}
build(...directives) {
return `${this.start};${super.build(...directives)}`;
}
cleanup(headers) {
}
}

View File

@ -1,24 +0,0 @@
var TabCache = (() => {
let cache = new Map();
browser.tabs.onUpdated.addListener(tab => {
cache.set(tab.id, tab);
});
browser.tabs.onRemoved.addListener(tab => {
cache.delete(tab.id);
});
(async () => {
for (let tab of await browser.tabs.query({})) {
cache.set(tab.id, tab);
}
})();
return {
get(tabId) {
return cache.get(tabId);
}
};
})();

View File

@ -1,49 +0,0 @@
class Timing {
constructor(workSlot = 10, longTime = 20000, pauseTime = 20) {
this.workSlot = workSlot;
this.longTime = longTime;
this.pauseTime = pauseTime;
this.interrupted = false;
this.fatalTimeout = false;
this.maxCalls = 1000;
this.reset();
}
static sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async pause() {
if (this.interrupted) throw new TimingException("Timing: interrupted");
let now = Date.now();
this.calls++;
let sinceLastCall = now - this.lastCall;
if (sinceLastCall > this.workSlot && this.calls > 1000) {
// low resolution (100ms) timer? Let's cap approximating by calls number
this.maxCalls = this.calls / sinceLastCall * this.workSlot;
}
this.lastCall = now;
this.elapsed = now - this.timeOrigin;
if (now - this.lastPause > this.workSlot || this.calls > this.maxCalls) {
this.tooLong = this.elapsed >= this.longTime;
if (this.tooLong && this.fatalTimeout) {
throw new TimingException(`Timing: exceeded ${this.longTime}ms timeout`);
}
this.calls = 0;
if (this.pauseTime > 0) await Timing.sleep(this.pauseTime);
this.lastPause = Date.now();
return true;
}
return false;
}
reset() {
this.elapsed = 0;
this.calls = 0;
this.timeOrigin = this.lastPause = this.lastCall = Date.now();
this.tooLong = false;
}
}
class TimingException extends Error {};

View File

@ -1,22 +0,0 @@
{
let mozWebExtUrl = document.URL.startsWith("moz-");
let isMozilla = mozWebExtUrl || typeof window.wrappedJSObject === "object";
let mobile = false;
if (isMozilla) {
if (mozWebExtUrl) {
// help browser-specific UI styling
document.documentElement.classList.add("mozwebext");
mobile = !("windows" in browser);
}
} else {
// shims for non-Mozilla browsers
if (typeof chrome === "object" && !chrome.tabs) {
// content script shims
}
}
var UA = {
isMozilla,
mobile,
};
}

View File

@ -1,45 +0,0 @@
"use strict";
class Ver {
constructor(version) {
if (version instanceof Ver) {
this.versionString = version.versionString;
this.parts = version.parts;
} else {
this.versionString = version.toString();
this.parts = this.versionString.split(".");
}
}
toString() {
return this.versionString;
}
compare(other) {
if (!(other instanceof Ver)) other = new Ver(other);
let p1 = this.parts, p2 = other.parts;
let maxParts = Math.max(p1.length, p2.length);
for (let j = 0; j < maxParts; j++) {
let s1 = p1[j] || "0";
let s2 = p2[j] || "0";
if (s1 === s2) continue;
let n1 = parseInt(s1);
let n2 = parseInt(s2);
if (n1 > n2) return 1;
if (n1 < n2) return -1;
// if numeric part is the same, an alphabetic suffix decreases value
// so a "pure number" wins
if (!/\D/.test(s1)) return 1;
if (!/\D/.test(s2)) return -1;
// both have an alhpabetic suffix, let's compare lexicographycally
if (s1 > s2) return 1;
if (s1 < s2) return -1;
}
return 0;
}
static is(ver1, op, ver2) {
let res = new Ver(ver1).compare(ver2);
return op.includes("!=") && res !== 0 ||
op.includes("=") && res === 0 ||
op.includes("<") && res === -1 ||
op.includes(">") && res === 1;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
var include = (() =>
{
let _inclusions = new Map();
function scriptLoader(src) {
let script = document.createElement("script");
script.src = src;
return script;
}
function styleLoader(src) {
let style = document.createElement("link");
style.rel = "stylesheet";
style.type = "text/css";
style.href = src;
return style;
}
return async function include(src) {
if (_inclusions.has(src)) return await _inclusions.get(src);
if (Array.isArray(src)) {
return await Promise.all(src.map(s => include(s)));
}
debug("Including", src);
let loading = new Promise((resolve, reject) => {
let inc = src.endsWith(".css") ? styleLoader(src) : scriptLoader(src);
inc.onload = () => resolve(inc);
inc.onerror = () => reject(new Error(`Failed to load ${src}`));
document.head.appendChild(inc);
});
_inclusions.set(src, loading);
return await (loading);
}
})();

View File

@ -1,19 +0,0 @@
{
let PREFIX = typeof browser === "object"
? `[${browser.runtime.getManifest().name}]` : '';
let debugCount = 0;
function log(msg, ...rest) {
console.log(`${PREFIX} ${msg}`, ...rest);
}
function debug(msg, ...rest) {
console.debug(`${PREFIX}:${debugCount++} ${msg}`, ...rest);
}
function error(e, msg, ...rest) {
console.error(`${PREFIX} ${msg}`, ...rest, e, e.message, e.stack);
}
}

View File

@ -1,31 +0,0 @@
{
// see https://bugzilla.mozilla.org/show_bug.cgi?id=1415644
let domains = UA.isMozilla ? [
"accounts-static.cdn.mozilla.net",
"accounts.firefox.com",
"addons.cdn.mozilla.net",
"addons.mozilla.org",
"api.accounts.firefox.com",
"content.cdn.mozilla.net",
"content.cdn.mozilla.net",
"discovery.addons.mozilla.org",
"input.mozilla.org",
"install.mozilla.org",
"oauth.accounts.firefox.com",
"profile.accounts.firefox.com",
"support.mozilla.org",
"sync.services.mozilla.com",
"testpilot.firefox.com",
] : [ "chrome.google.com" ];
function isRestrictedURL(u) {
try {
if (typeof u === "string") u = new URL(u);
let {protocol, hostname} = u;
return (!/^(?:https?|file|data):$/.test(protocol))
|| protocol === "https:" && hostname && domains.includes(tld.normalize(hostname));
} catch (e) {
return false;
}
}
}

View File

@ -1,518 +0,0 @@
/**
* [js-sha256]{@link https://github.com/emn178/js-sha256}
*
* @version 0.9.0
* @author Chen, Yi-Cyuan [emn178@gmail.com]
* @copyright Chen, Yi-Cyuan 2014-2017
* @license MIT
*/
/*jslint bitwise: true */
(function () {
'use strict';
var ERROR = 'input is invalid type';
var WINDOW = typeof window === 'object';
var root = WINDOW ? window : {};
if (root.JS_SHA256_NO_WINDOW) {
WINDOW = false;
}
var WEB_WORKER = !WINDOW && typeof self === 'object';
var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
if (NODE_JS) {
root = global;
} else if (WEB_WORKER) {
root = self;
}
var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports;
var AMD = typeof define === 'function' && define.amd;
var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
var HEX_CHARS = '0123456789abcdef'.split('');
var EXTRA = [-2147483648, 8388608, 32768, 128];
var SHIFT = [24, 16, 8, 0];
var K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];
var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer'];
var blocks = [];
if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) {
Array.isArray = function (obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
}
if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
ArrayBuffer.isView = function (obj) {
return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
};
}
var createOutputMethod = function (outputType, is224) {
return function (message) {
return new Sha256(is224, true).update(message)[outputType]();
};
};
var createMethod = function (is224) {
var method = createOutputMethod('hex', is224);
if (NODE_JS) {
method = nodeWrap(method, is224);
}
method.create = function () {
return new Sha256(is224);
};
method.update = function (message) {
return method.create().update(message);
};
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
var type = OUTPUT_TYPES[i];
method[type] = createOutputMethod(type, is224);
}
return method;
};
var nodeWrap = function (method, is224) {
var crypto = eval("require('crypto')");
var Buffer = eval("require('buffer').Buffer");
var algorithm = is224 ? 'sha224' : 'sha256';
var nodeMethod = function (message) {
if (typeof message === 'string') {
return crypto.createHash(algorithm).update(message, 'utf8').digest('hex');
} else {
if (message === null || message === undefined) {
throw new Error(ERROR);
} else if (message.constructor === ArrayBuffer) {
message = new Uint8Array(message);
}
}
if (Array.isArray(message) || ArrayBuffer.isView(message) ||
message.constructor === Buffer) {
return crypto.createHash(algorithm).update(new Buffer(message)).digest('hex');
} else {
return method(message);
}
};
return nodeMethod;
};
var createHmacOutputMethod = function (outputType, is224) {
return function (key, message) {
return new HmacSha256(key, is224, true).update(message)[outputType]();
};
};
var createHmacMethod = function (is224) {
var method = createHmacOutputMethod('hex', is224);
method.create = function (key) {
return new HmacSha256(key, is224);
};
method.update = function (key, message) {
return method.create(key).update(message);
};
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
var type = OUTPUT_TYPES[i];
method[type] = createHmacOutputMethod(type, is224);
}
return method;
};
function Sha256(is224, sharedMemory) {
if (sharedMemory) {
blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
this.blocks = blocks;
} else {
this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
}
if (is224) {
this.h0 = 0xc1059ed8;
this.h1 = 0x367cd507;
this.h2 = 0x3070dd17;
this.h3 = 0xf70e5939;
this.h4 = 0xffc00b31;
this.h5 = 0x68581511;
this.h6 = 0x64f98fa7;
this.h7 = 0xbefa4fa4;
} else { // 256
this.h0 = 0x6a09e667;
this.h1 = 0xbb67ae85;
this.h2 = 0x3c6ef372;
this.h3 = 0xa54ff53a;
this.h4 = 0x510e527f;
this.h5 = 0x9b05688c;
this.h6 = 0x1f83d9ab;
this.h7 = 0x5be0cd19;
}
this.block = this.start = this.bytes = this.hBytes = 0;
this.finalized = this.hashed = false;
this.first = true;
this.is224 = is224;
}
Sha256.prototype.update = function (message) {
if (this.finalized) {
return;
}
var notString, type = typeof message;
if (type !== 'string') {
if (type === 'object') {
if (message === null) {
throw new Error(ERROR);
} else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
message = new Uint8Array(message);
} else if (!Array.isArray(message)) {
if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
throw new Error(ERROR);
}
}
} else {
throw new Error(ERROR);
}
notString = true;
}
var code, index = 0, i, length = message.length, blocks = this.blocks;
while (index < length) {
if (this.hashed) {
this.hashed = false;
blocks[0] = this.block;
blocks[16] = blocks[1] = blocks[2] = blocks[3] =
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
}
if (notString) {
for (i = this.start; index < length && i < 64; ++index) {
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
}
} else {
for (i = this.start; index < length && i < 64; ++index) {
code = message.charCodeAt(index);
if (code < 0x80) {
blocks[i >> 2] |= code << SHIFT[i++ & 3];
} else if (code < 0x800) {
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
} else if (code < 0xd800 || code >= 0xe000) {
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
}
}
}
this.lastByteIndex = i;
this.bytes += i - this.start;
if (i >= 64) {
this.block = blocks[16];
this.start = i - 64;
this.hash();
this.hashed = true;
} else {
this.start = i;
}
}
if (this.bytes > 4294967295) {
this.hBytes += this.bytes / 4294967296 << 0;
this.bytes = this.bytes % 4294967296;
}
return this;
};
Sha256.prototype.finalize = function () {
if (this.finalized) {
return;
}
this.finalized = true;
var blocks = this.blocks, i = this.lastByteIndex;
blocks[16] = this.block;
blocks[i >> 2] |= EXTRA[i & 3];
this.block = blocks[16];
if (i >= 56) {
if (!this.hashed) {
this.hash();
}
blocks[0] = this.block;
blocks[16] = blocks[1] = blocks[2] = blocks[3] =
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
}
blocks[14] = this.hBytes << 3 | this.bytes >>> 29;
blocks[15] = this.bytes << 3;
this.hash();
};
Sha256.prototype.hash = function () {
var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4, f = this.h5, g = this.h6,
h = this.h7, blocks = this.blocks, j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc;
for (j = 16; j < 64; ++j) {
// rightrotate
t1 = blocks[j - 15];
s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3);
t1 = blocks[j - 2];
s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ (t1 >>> 10);
blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0;
}
bc = b & c;
for (j = 0; j < 64; j += 4) {
if (this.first) {
if (this.is224) {
ab = 300032;
t1 = blocks[0] - 1413257819;
h = t1 - 150054599 << 0;
d = t1 + 24177077 << 0;
} else {
ab = 704751109;
t1 = blocks[0] - 210244248;
h = t1 - 1521486534 << 0;
d = t1 + 143694565 << 0;
}
this.first = false;
} else {
s0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
s1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
ab = a & b;
maj = ab ^ (a & c) ^ bc;
ch = (e & f) ^ (~e & g);
t1 = h + s1 + ch + K[j] + blocks[j];
t2 = s0 + maj;
h = d + t1 << 0;
d = t1 + t2 << 0;
}
s0 = ((d >>> 2) | (d << 30)) ^ ((d >>> 13) | (d << 19)) ^ ((d >>> 22) | (d << 10));
s1 = ((h >>> 6) | (h << 26)) ^ ((h >>> 11) | (h << 21)) ^ ((h >>> 25) | (h << 7));
da = d & a;
maj = da ^ (d & b) ^ ab;
ch = (h & e) ^ (~h & f);
t1 = g + s1 + ch + K[j + 1] + blocks[j + 1];
t2 = s0 + maj;
g = c + t1 << 0;
c = t1 + t2 << 0;
s0 = ((c >>> 2) | (c << 30)) ^ ((c >>> 13) | (c << 19)) ^ ((c >>> 22) | (c << 10));
s1 = ((g >>> 6) | (g << 26)) ^ ((g >>> 11) | (g << 21)) ^ ((g >>> 25) | (g << 7));
cd = c & d;
maj = cd ^ (c & a) ^ da;
ch = (g & h) ^ (~g & e);
t1 = f + s1 + ch + K[j + 2] + blocks[j + 2];
t2 = s0 + maj;
f = b + t1 << 0;
b = t1 + t2 << 0;
s0 = ((b >>> 2) | (b << 30)) ^ ((b >>> 13) | (b << 19)) ^ ((b >>> 22) | (b << 10));
s1 = ((f >>> 6) | (f << 26)) ^ ((f >>> 11) | (f << 21)) ^ ((f >>> 25) | (f << 7));
bc = b & c;
maj = bc ^ (b & d) ^ cd;
ch = (f & g) ^ (~f & h);
t1 = e + s1 + ch + K[j + 3] + blocks[j + 3];
t2 = s0 + maj;
e = a + t1 << 0;
a = t1 + t2 << 0;
}
this.h0 = this.h0 + a << 0;
this.h1 = this.h1 + b << 0;
this.h2 = this.h2 + c << 0;
this.h3 = this.h3 + d << 0;
this.h4 = this.h4 + e << 0;
this.h5 = this.h5 + f << 0;
this.h6 = this.h6 + g << 0;
this.h7 = this.h7 + h << 0;
};
Sha256.prototype.hex = function () {
this.finalize();
var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5,
h6 = this.h6, h7 = this.h7;
var hex = HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] +
HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] +
HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] +
HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +
HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] +
HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] +
HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] +
HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +
HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] +
HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] +
HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] +
HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +
HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] +
HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] +
HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] +
HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +
HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] +
HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] +
HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] +
HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] +
HEX_CHARS[(h5 >> 28) & 0x0F] + HEX_CHARS[(h5 >> 24) & 0x0F] +
HEX_CHARS[(h5 >> 20) & 0x0F] + HEX_CHARS[(h5 >> 16) & 0x0F] +
HEX_CHARS[(h5 >> 12) & 0x0F] + HEX_CHARS[(h5 >> 8) & 0x0F] +
HEX_CHARS[(h5 >> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] +
HEX_CHARS[(h6 >> 28) & 0x0F] + HEX_CHARS[(h6 >> 24) & 0x0F] +
HEX_CHARS[(h6 >> 20) & 0x0F] + HEX_CHARS[(h6 >> 16) & 0x0F] +
HEX_CHARS[(h6 >> 12) & 0x0F] + HEX_CHARS[(h6 >> 8) & 0x0F] +
HEX_CHARS[(h6 >> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F];
if (!this.is224) {
hex += HEX_CHARS[(h7 >> 28) & 0x0F] + HEX_CHARS[(h7 >> 24) & 0x0F] +
HEX_CHARS[(h7 >> 20) & 0x0F] + HEX_CHARS[(h7 >> 16) & 0x0F] +
HEX_CHARS[(h7 >> 12) & 0x0F] + HEX_CHARS[(h7 >> 8) & 0x0F] +
HEX_CHARS[(h7 >> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F];
}
return hex;
};
Sha256.prototype.toString = Sha256.prototype.hex;
Sha256.prototype.digest = function () {
this.finalize();
var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5,
h6 = this.h6, h7 = this.h7;
var arr = [
(h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF,
(h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF,
(h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF,
(h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF,
(h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF,
(h5 >> 24) & 0xFF, (h5 >> 16) & 0xFF, (h5 >> 8) & 0xFF, h5 & 0xFF,
(h6 >> 24) & 0xFF, (h6 >> 16) & 0xFF, (h6 >> 8) & 0xFF, h6 & 0xFF
];
if (!this.is224) {
arr.push((h7 >> 24) & 0xFF, (h7 >> 16) & 0xFF, (h7 >> 8) & 0xFF, h7 & 0xFF);
}
return arr;
};
Sha256.prototype.array = Sha256.prototype.digest;
Sha256.prototype.arrayBuffer = function () {
this.finalize();
var buffer = new ArrayBuffer(this.is224 ? 28 : 32);
var dataView = new DataView(buffer);
dataView.setUint32(0, this.h0);
dataView.setUint32(4, this.h1);
dataView.setUint32(8, this.h2);
dataView.setUint32(12, this.h3);
dataView.setUint32(16, this.h4);
dataView.setUint32(20, this.h5);
dataView.setUint32(24, this.h6);
if (!this.is224) {
dataView.setUint32(28, this.h7);
}
return buffer;
};
function HmacSha256(key, is224, sharedMemory) {
var i, type = typeof key;
if (type === 'string') {
var bytes = [], length = key.length, index = 0, code;
for (i = 0; i < length; ++i) {
code = key.charCodeAt(i);
if (code < 0x80) {
bytes[index++] = code;
} else if (code < 0x800) {
bytes[index++] = (0xc0 | (code >> 6));
bytes[index++] = (0x80 | (code & 0x3f));
} else if (code < 0xd800 || code >= 0xe000) {
bytes[index++] = (0xe0 | (code >> 12));
bytes[index++] = (0x80 | ((code >> 6) & 0x3f));
bytes[index++] = (0x80 | (code & 0x3f));
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff));
bytes[index++] = (0xf0 | (code >> 18));
bytes[index++] = (0x80 | ((code >> 12) & 0x3f));
bytes[index++] = (0x80 | ((code >> 6) & 0x3f));
bytes[index++] = (0x80 | (code & 0x3f));
}
}
key = bytes;
} else {
if (type === 'object') {
if (key === null) {
throw new Error(ERROR);
} else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) {
key = new Uint8Array(key);
} else if (!Array.isArray(key)) {
if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) {
throw new Error(ERROR);
}
}
} else {
throw new Error(ERROR);
}
}
if (key.length > 64) {
key = (new Sha256(is224, true)).update(key).array();
}
var oKeyPad = [], iKeyPad = [];
for (i = 0; i < 64; ++i) {
var b = key[i] || 0;
oKeyPad[i] = 0x5c ^ b;
iKeyPad[i] = 0x36 ^ b;
}
Sha256.call(this, is224, sharedMemory);
this.update(iKeyPad);
this.oKeyPad = oKeyPad;
this.inner = true;
this.sharedMemory = sharedMemory;
}
HmacSha256.prototype = new Sha256();
HmacSha256.prototype.finalize = function () {
Sha256.prototype.finalize.call(this);
if (this.inner) {
this.inner = false;
var innerHash = this.array();
Sha256.call(this, this.is224, this.sharedMemory);
this.update(this.oKeyPad);
this.update(innerHash);
Sha256.prototype.finalize.call(this);
}
};
var exports = createMethod();
exports.sha256 = exports;
exports.sha224 = createMethod(true);
exports.sha256.hmac = createHmacMethod();
exports.sha224.hmac = createHmacMethod(true);
if (COMMON_JS) {
module.exports = exports;
} else {
root.sha256 = exports.sha256;
root.sha224 = exports.sha224;
if (AMD) {
define(function () {
return exports;
});
}
}
})();

View File

@ -1,15 +0,0 @@
'use strict';
function uuid() {
try {
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,
c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4)
.toString(16));
} catch (e) {
// fallback, as the Tor Browser seems to fail above
uuid = () => 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
}

View File

@ -34,30 +34,30 @@
"background": {
"persistent": true,
"scripts": [
"lib/UA.js",
"lib/browser-polyfill.js",
"lib/uuid.js",
"nscl/common/SyncMessage.js",
"lib/log.js",
"lib/include.js",
"nscl/lib/punycode.js",
"nscl/common/tld.js",
"lib/LastListener.js",
"nscl/common/Messages.js",
"lib/CSP.js",
"lib/NetCSP.js",
"lib/TabCache.js",
"common/CapsCSP.js",
"/nscl/lib/browser-polyfill.js",
"/nscl/lib/punycode.js",
"/nscl/common/UA.js",
"/nscl/common/uuid.js",
"/nscl/common/SyncMessage.js",
"/nscl/common/log.js",
"/nscl/common/tld.js",
"/nscl/common/Messages.js",
"/nscl/common/CSP.js",
"/nscl/common/NetCSP.js",
"/nscl/common/CapsCSP.js",
"/nscl/common/RequestKey.js",
"/nscl/common/Sites.js",
"/nscl/common/Permissions.js",
"/nscl/common/Policy.js",
"common/locale.js",
"common/SyntaxChecker.js",
"common/Storage.js",
"/nscl/common/locale.js",
"/nscl/common/SyntaxChecker.js",
"/nscl/common/Storage.js",
"/nscl/common/include.js",
"/nscl/service/LastListener.js",
"/nscl/service/prefetchCSSResources.js",
"/nscl/service/TabCache.js",
"ui/Prompts.js",
"xss/XSS.js",
"/nscl/service/prefetchCSSResources.js",
"bg/ReportingCSP.js",
"bg/deferWebTraffic.js",
"bg/Defaults.js",
@ -91,28 +91,28 @@
"match_about_blank": true,
"all_frames": true,
"js": [
"lib/UA.js",
"lib/browser-polyfill.js",
"nscl/common/SyncMessage.js",
"lib/log.js",
"lib/uuid.js",
"lib/sha256.js",
"nscl/common/Messages.js",
"lib/CSP.js",
"nscl/content/patchWindow.js",
"common/CapsCSP.js",
"nscl/common/RequestKey.js",
"content/DocumentCSP.js",
"nscl/content/NoscriptElements.js",
"/nscl/lib/browser-polyfill.js",
"/nscl/lib/sha256.js",
"/nscl/common/UA.js",
"/nscl/common/uuid.js",
"/nscl/common/log.js",
"/nscl/common/SyncMessage.js",
"/nscl/common/Messages.js",
"/nscl/common/CSP.js",
"/nscl/common/CapsCSP.js",
"/nscl/common/RequestKey.js",
"/nscl/content/patchWindow.js",
"/nscl/content/DocumentCSP.js",
"/nscl/content/NoscriptElements.js",
"/nscl/content/prefetchCSSResources.js",
"/nscl/content/PlaceHolder.js",
"/nscl/content/sanitizePaste.js",
"content/onScriptDisabled.js",
"content/staticNS.js",
"content/PlaceHolder.js",
"content/content.js",
"/nscl/content/media.js",
"/nscl/content/webglHook.js",
"content/embeddingDocument.js",
"content/webglHook.js",
"content/media.js",
"content/sanitizePaste.js"
"content/content.js"
]
},

@ -1 +1 @@
Subproject commit 22cc68f13021f67e546bcba27ebf86a7e3a55881
Subproject commit 745b8d07daa9cbeab399786969cf2bf761ad228c

View File

@ -8,12 +8,12 @@
<link rel="stylesheet" href="/lib/flextabs.css" />
<link rel="stylesheet" href="options.css" />
<link rel="stylesheet" href="whirlpool.css" />
<script src="/lib/UA.js"></script>
<script src="/lib/browser-polyfill.js"></script>
<script src="/lib/include.js"></script>
<script src="/lib/log.js"></script>
<script src="/nscl/lib/browser-polyfill.js"></script>
<script src="/nscl/common/UA.js"></script>
<script src="/nscl/common/include.js"></script>
<script src="/nscl/common/log.js"></script>
<script src="/nscl/common/locale.js"></script>
<script src="/lib/flextabs.js"></script>
<script src="/common/locale.js"></script>
<script src="/ui/ui.js"></script>
</head>
<body id="noscript-options">

View File

@ -6,11 +6,11 @@
<title>NoScript Settings</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="popup.css" />
<script src="/lib/UA.js"></script>
<script src="/lib/browser-polyfill.js"></script>
<script src="/lib/include.js"></script>
<script src="/lib/log.js"></script>
<script src="/common/locale.js"></script>
<script src="/nscl/lib/browser-polyfill.js"></script>
<script src="/nscl/common/UA.js"></script>
<script src="/nscl/common/include.js"></script>
<script src="/nscl/common/log.js"></script>
<script src="/nscl/common/locale.js"></script>
<script src="/ui/ui.js"></script>
</head>

View File

@ -208,7 +208,7 @@ addEventListener("unload", e => {
error(e, "Could not run scripts on %s: privileged page?", pageTab.url);
}
await include("/lib/restricted.js");
await include("/nscl/common/restricted.js");
let isRestricted = isRestrictedURL(pageTab.url);
if (!isHttp || isRestricted) {
showMessage("warning", _("privilegedPage"));

View File

@ -5,11 +5,11 @@
<title></title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="prompt.css" />
<script src="/lib/UA.js"></script>
<script src="/lib/browser-polyfill.js"></script>
<script src="/lib/include.js"></script>
<script src="/lib/log.js"></script>
<script src="/common/locale.js"></script>
<script src="/nscl/lib/browser-polyfill.js"></script>
<script src="/nscl/common/UA.js"></script>
<script src="/nscl/common/include.js"></script>
<script src="/nscl/common/log.js"></script>
<script src="/nscl/common/locale.js"></script>
<script src="/ui/resize_hack.js"></script>
</head>
<body>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<meta charset="utf-8">
<script src="/lib/UA.js"></script>
<script src="/lib/browser-polyfill.js"></script>
<script src="/lib/log.js"></script>
<script src="/lib/include.js"></script>
<script src="/nscl/lib/browser-polyfill.js"></script>
<script src="/nscl/common/UA.js"></script>
<script src="/nscl/common/include.js"></script>
<script src="/nscl/common/log.js"></script>
<script src="siteInfo.js"></script>

View File

@ -4,7 +4,7 @@ let include = src => {
}
let XSS = {};
include("/lib/log.js");
include("/nscl/common/log.js");
for (let logType of ["log", "debug", "error"]) {
this[logType] = (...log) => {

View File

@ -1,8 +1,8 @@
XSS.InjectionChecker = (async () => {
await include([
"/common/SyntaxChecker.js",
"/lib/Base64.js",
"/lib/Timing.js",
"/nscl/common/SyntaxChecker.js",
"/nscl/common/Base64.js",
"/nscl/common/Timing.js",
"/xss/FlashIdiocy.js",
"/xss/ASPIdiocy.js",
"/lib/he.js"]