[nscl] More refactoring out in NoScript Commons Library.
This commit is contained in:
parent
4c9ddc847a
commit
c30c9c5627
|
@ -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;
|
||||
|
|
|
@ -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 = () => {};
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
};
|
||||
})()
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
})()
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
})();
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
})();
|
|
@ -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 {};
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
})();
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
},
|
||||
|
||||
|
|
2
src/nscl
2
src/nscl
|
@ -1 +1 @@
|
|||
Subproject commit 22cc68f13021f67e546bcba27ebf86a7e3a55881
|
||||
Subproject commit 745b8d07daa9cbeab399786969cf2bf761ad228c
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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"]
|
||||
|
|
Loading…
Reference in New Issue