large re-org and refactor of code, initial work on dynamically setting the sha256-<hash> CSP http policy to fix cookie setting
This commit is contained in:
parent
833cc256de
commit
c4e524a69f
19 changed files with 688 additions and 588 deletions
|
@ -3,7 +3,9 @@
|
|||
(function () {
|
||||
"use strict";
|
||||
|
||||
const {packingLib, standards, storageLib, domainMatcherLib} = window.WEB_API_MANAGER;
|
||||
const {storageLib, domainMatcherLib, constants} = window.WEB_API_MANAGER;
|
||||
const {cookieEncodingLib, proxyBlockLib, httpHeadersLib} = window.WEB_API_MANAGER;
|
||||
const {standards} = window.WEB_API_MANAGER;
|
||||
const rootObject = window.browser || window.chrome;
|
||||
const defaultKey = "(default)";
|
||||
|
||||
|
@ -48,7 +50,6 @@
|
|||
tabId: tabId
|
||||
});
|
||||
rootObject.browserAction.enable();
|
||||
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -124,22 +125,50 @@
|
|||
// of the URL being requested.
|
||||
const matchingDomainRule = domainMatcherLib.matchUrl(Object.keys(domainRules), url);
|
||||
const standardsToBlock = domainRules[matchingDomainRule || defaultKey];
|
||||
const shouldLogOption = ["shouldLog"];
|
||||
const encodedOptions = cookieEncodingLib.toCookieValue(standardsToBlock, shouldLog);
|
||||
|
||||
const options = Object.keys(standards).concat(shouldLogOption);
|
||||
const standardsToBlockWithShouldLogOption = shouldLog
|
||||
? standardsToBlock.concat(shouldLogOption)
|
||||
: standardsToBlock;
|
||||
// If we're on a site thats sending the "strict-dynamic"
|
||||
// Content-Security-Policy instruction, then we need to add the
|
||||
// injected proxy code to the list of scripts that are allowed to
|
||||
// run in the page.
|
||||
const cspDynamicPolicyHeaders = details.responseHeaders
|
||||
.filter(httpHeadersLib.isHeaderCSP)
|
||||
.filter(httpHeadersLib.isCSPHeaderSettingStrictDynamic);
|
||||
|
||||
const packedValues = packingLib.pack(
|
||||
options,
|
||||
standardsToBlockWithShouldLogOption
|
||||
);
|
||||
if (cspDynamicPolicyHeaders.length === 1) {
|
||||
const [ignore, scriptHash] = proxyBlockLib.generateScriptPayload(
|
||||
standards,
|
||||
standardsToBlock,
|
||||
shouldLog
|
||||
);
|
||||
|
||||
const newCSPValue = httpHeadersLib.createCSPInstructionWithHashAllowed(
|
||||
cspDynamicPolicyHeaders[0].value,
|
||||
"sha256-" + scriptHash
|
||||
);
|
||||
|
||||
if (newCSPValue !== false) {
|
||||
cspDynamicPolicyHeaders[0].value = newCSPValue;
|
||||
}
|
||||
}
|
||||
|
||||
details.responseHeaders.push({
|
||||
name: "Set-Cookie",
|
||||
value: `wam-temp-cookie=${packedValues}`
|
||||
});
|
||||
// If there is already a set-cookie instruction being issued,
|
||||
// don't overwrite it, but add our cookie to the end of it. Otherwise,
|
||||
// create a new set-cookie instruction header.
|
||||
const webAPIStandardsCookie = `${constants.cookieName}=${encodedOptions}`;
|
||||
const setCookieHeaders = details.responseHeaders.filter(httpHeadersLib.isSetCookie);
|
||||
|
||||
if (setCookieHeaders.length > 0) {
|
||||
|
||||
setCookieHeaders[0].value += "; " + webAPIStandardsCookie;
|
||||
|
||||
} else {
|
||||
|
||||
details.responseHeaders.push({
|
||||
name: "Set-Cookie",
|
||||
value: webAPIStandardsCookie
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
responseHeaders: details.responseHeaders
|
||||
|
|
|
@ -37,8 +37,8 @@
|
|||
</div>
|
||||
</section>
|
||||
<script src="../lib/init.js"></script>
|
||||
<script src="../content_scripts/dist/defaults.js"></script>
|
||||
<script src="../content_scripts/dist/standards.js"></script>
|
||||
<script src="../lib/defaults.js"></script>
|
||||
<script src="../data/standards.js"></script>
|
||||
<script src="../lib/storage.js"></script>
|
||||
<script src="js/lib/vue.js"></script>
|
||||
<script src="js/state.js"></script>
|
||||
|
|
2
content_scripts/dist/README.md
vendored
2
content_scripts/dist/README.md
vendored
|
@ -1,2 +0,0 @@
|
|||
This directory contains script files that are build from gulp, and which are
|
||||
injected into visited frames / pages.
|
29
content_scripts/instrument.js
Normal file
29
content_scripts/instrument.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*jslint es6: true, browser: true*/
|
||||
/*global window*/
|
||||
// This script file runs in the context of the extension, and mainly
|
||||
// exists to inject the proxy blocking code into content frames.
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const cookies2 = window.Cookies.noConflict();
|
||||
const {standards, constants, cookieEncodingLib, proxyBlockLib} = window.WEB_API_MANAGER;
|
||||
const standardsCookieName = constants.cookieName;
|
||||
|
||||
const doc = window.document;
|
||||
const script = doc.createElement('script');
|
||||
const rootElm = doc.head || doc.documentElement;
|
||||
|
||||
const cookieValue = cookies2.get(standardsCookieName);
|
||||
const [standardsToBlock, shouldLog] = cookieEncodingLib.fromCookieValue(cookieValue);
|
||||
cookies2.remove(standardsCookieName);
|
||||
|
||||
const [scriptToInject, scriptHash] = proxyBlockLib.generateScriptPayload(
|
||||
standards,
|
||||
standardsToBlock,
|
||||
shouldLog
|
||||
);
|
||||
|
||||
script.appendChild(doc.createTextNode(scriptToInject));
|
||||
script.integrity = "sha256-" + scriptHash;
|
||||
rootElm.appendChild(script);
|
||||
}());
|
|
@ -1,43 +0,0 @@
|
|||
/*jslint es6: true, browser: true*/
|
||||
/*global window, Cookies*/
|
||||
// This script file runs in the context of the extension, and mainly
|
||||
// exists to inject the proxy blocking code into content frames.
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const doc = window.document;
|
||||
const script = doc.createElement('script');
|
||||
const rootElm = doc.head || doc.documentElement;
|
||||
const shouldLogValue = "shouldLog";
|
||||
|
||||
const standardsCookieKey = "wam-temp-cookie";
|
||||
const {packingLib, standards} = window.WEB_API_MANAGER;
|
||||
const options = Object.keys(standards);
|
||||
const optionsWithShouldLog = options.concat([shouldLogValue]);
|
||||
const packedValues = Cookies.get(standardsCookieKey);
|
||||
const unpackedValues = packingLib.unpack(optionsWithShouldLog, packedValues);
|
||||
Cookies.remove(standardsCookieKey);
|
||||
|
||||
let shouldLog;
|
||||
const standardsToBlock = unpackedValues;
|
||||
const indexOfShouldLog = unpackedValues.indexOf(shouldLogValue);
|
||||
|
||||
if (indexOfShouldLog === -1) {
|
||||
shouldLog = false;
|
||||
} else {
|
||||
shouldLog = true;
|
||||
standardsToBlock.splice(indexOfShouldLog, 1);
|
||||
}
|
||||
|
||||
const code = `
|
||||
window.WEB_API_MANAGER_PAGE = {
|
||||
standards: ${JSON.stringify(standards)},
|
||||
toBlock: ${JSON.stringify(standardsToBlock)},
|
||||
shouldLog: ${shouldLog}
|
||||
};
|
||||
###-INJECTED-PROXY-BLOCKING-CODE-###
|
||||
`;
|
||||
|
||||
script.appendChild(doc.createTextNode(code));
|
||||
rootElm.appendChild(script);
|
||||
}());
|
|
@ -1,174 +0,0 @@
|
|||
/*jslint es6: true, browser: true*/
|
||||
/*global window*/
|
||||
// The contents of this file are programatically injected into all frames.
|
||||
// It is compiled into the src/instrument.js file to create the
|
||||
// dist/instrument.js script/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const settings = window.WEB_API_MANAGER_PAGE;
|
||||
const shouldLog = settings.shouldLog;
|
||||
const standardsToBlock = settings.toBlock;
|
||||
const standardDefinitions = settings.standards;
|
||||
const hostName = window.location.hostname;
|
||||
|
||||
// Its possible that the Web API removal code will block direct references
|
||||
// to the following methods, so grab references to them before the
|
||||
// DOM is instrumented (and their references are possibly blocked).
|
||||
const removeChild = window.Element.prototype.removeChild;
|
||||
const getElementsByTagName = window.document.getElementsByTagName;
|
||||
|
||||
const defaultFunction = function () {};
|
||||
const funcPropNames = Object.getOwnPropertyNames(defaultFunction);
|
||||
const unconfigurablePropNames = funcPropNames.filter(function (propName) {
|
||||
const possiblePropDesc = Object.getOwnPropertyDescriptor(defaultFunction, propName);
|
||||
return (possiblePropDesc && !possiblePropDesc.configurable);
|
||||
});
|
||||
|
||||
const featuresToBlock = standardsToBlock.reduce(function (prev, cur) {
|
||||
return prev.concat(standardDefinitions[cur].features);
|
||||
}, []);
|
||||
|
||||
const toPrimitiveFunc = function (hint) {
|
||||
if (hint === "number" || hint === "default") {
|
||||
return 0;
|
||||
}
|
||||
if (hint === "string") {
|
||||
return "";
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const keyPathToRefPath = function (keyPath) {
|
||||
const keyParts = keyPath.split(".");
|
||||
return keyParts.reduce(function (prev, cur) {
|
||||
|
||||
if (prev === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const numNodes = prev.length;
|
||||
const currentLeaf = (numNodes === 0)
|
||||
? window
|
||||
: prev[numNodes - 1];
|
||||
const nextLeaf = currentLeaf[cur];
|
||||
|
||||
if (nextLeaf === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return prev.concat([nextLeaf]);
|
||||
}, []);
|
||||
};
|
||||
|
||||
const createBlockingProxy = function (keyPath) {
|
||||
|
||||
let hasBeenLogged = false;
|
||||
|
||||
const logKeyPath = function () {
|
||||
|
||||
if (keyPath !== undefined &&
|
||||
hasBeenLogged === false &&
|
||||
shouldLog) {
|
||||
hasBeenLogged = true;
|
||||
console.log("Blocked '" + keyPath + "' on '" + hostName + "'");
|
||||
}
|
||||
};
|
||||
|
||||
let blockingProxy;
|
||||
blockingProxy = new Proxy(defaultFunction, {
|
||||
get: function (ignore, property) {
|
||||
logKeyPath();
|
||||
|
||||
if (property === Symbol.toPrimitive) {
|
||||
return toPrimitiveFunc;
|
||||
}
|
||||
|
||||
if (property === "valueOf") {
|
||||
return toPrimitiveFunc;
|
||||
}
|
||||
|
||||
return blockingProxy;
|
||||
},
|
||||
set: function () {
|
||||
logKeyPath();
|
||||
return blockingProxy;
|
||||
},
|
||||
apply: function () {
|
||||
logKeyPath();
|
||||
return blockingProxy;
|
||||
},
|
||||
ownKeys: function (ignore) {
|
||||
return unconfigurablePropNames;
|
||||
},
|
||||
has: function (ignore, property) {
|
||||
return (unconfigurablePropNames.indexOf(property) > -1);
|
||||
},
|
||||
getOwnPropertyDescriptor: function (ignore, property) {
|
||||
if (unconfigurablePropNames.indexOf(property) === -1) {
|
||||
return undefined;
|
||||
}
|
||||
return Object.getOwnPropertyDescriptor(defaultFunction, property);
|
||||
}
|
||||
});
|
||||
|
||||
return blockingProxy;
|
||||
};
|
||||
|
||||
const defaultBlockingProxy = createBlockingProxy();
|
||||
|
||||
const blockFeatureAtKeyPath = function (keyPath) {
|
||||
const propertyRefs = keyPathToRefPath(keyPath);
|
||||
|
||||
// If we weren't able to turn the key path into an array of references,
|
||||
// then it means that the property doesn't exist in this DOM /
|
||||
// environment, so there is nothing to block.
|
||||
if (propertyRefs === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const keyPathSegments = keyPath.split(".");
|
||||
const lastPropertyName = keyPathSegments[keyPathSegments.length - 1];
|
||||
const leafRef = propertyRefs[propertyRefs.length - 1];
|
||||
const parentRef = propertyRefs[propertyRefs.length - 2];
|
||||
|
||||
// At least for now, only interpose on methods.
|
||||
if (typeof leafRef !== "function") {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (shouldLog === true) {
|
||||
parentRef[lastPropertyName] = createBlockingProxy(keyPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
parentRef[lastPropertyName] = defaultBlockingProxy;
|
||||
return true;
|
||||
|
||||
} catch (e) {
|
||||
|
||||
if (shouldLog) {
|
||||
console.log("Error instrumenting " + keyPath + ": " + e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
featuresToBlock.forEach(blockFeatureAtKeyPath);
|
||||
|
||||
// Next, delete the WEB_API_MANAGER_PAGE global property. Technically
|
||||
// this never needed to be global, but doing so allows for easier
|
||||
// jslinting of the code, makes things easier to understand (for me
|
||||
// at least) and doesn't have any side effect as long as we delete
|
||||
// it when we're done, and before the page scripts can start running.
|
||||
delete window.WEB_API_MANAGER_PAGE;
|
||||
|
||||
// Last, remove the script tag containing this code from the document,
|
||||
// so that the structure of the page looks like what the page author
|
||||
// expects / intended.
|
||||
const scriptTags = getElementsByTagName.call(window.document, "script");
|
||||
removeChild.call(scriptTags[0].parentNode, scriptTags[0]);
|
||||
}());
|
2
data/standards.js
Normal file
2
data/standards.js
Normal file
File diff suppressed because one or more lines are too long
32
gulpfile.js
32
gulpfile.js
|
@ -1,6 +1,5 @@
|
|||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const UglifyJS = require("uglify-es");
|
||||
|
||||
gulp.task('default', function () {
|
||||
|
||||
|
@ -36,34 +35,5 @@ gulp.task('default', function () {
|
|||
|
||||
const renderedStandardsModule = builtScriptComment + `window.WEB_API_MANAGER.standards = ${JSON.stringify(combinedStandards)};`;
|
||||
|
||||
fs.writeFileSync("content_scripts/dist/standards.js", renderedStandardsModule);
|
||||
|
||||
const proxyBlockSrc = fs.readFileSync("content_scripts/src/proxyblock.js", "utf8");
|
||||
const instrumentSrc = fs.readFileSync("content_scripts/src/instrument.js", "utf8");
|
||||
|
||||
const stripCommentsFromSource = function (source) {
|
||||
const fileLines = source.split("\n");
|
||||
const linesWithoutComments = fileLines.filter(aLine => !isLineAComment(aLine));
|
||||
return linesWithoutComments.join("\n");
|
||||
};
|
||||
|
||||
const proxyBlockSrcWOComments = stripCommentsFromSource(proxyBlockSrc);
|
||||
const instrumentSrcWOComments = stripCommentsFromSource(instrumentSrc);
|
||||
|
||||
const proxyBlockSrcWithHeader = "/** This code is a minified version of content_scripts/src/proxyblock.js **/\n" + proxyBlockSrcWOComments;
|
||||
const instrumentSrcWithProxyInjected = instrumentSrcWOComments.replace(
|
||||
"###-INJECTED-PROXY-BLOCKING-CODE-###",
|
||||
UglifyJS.minify(proxyBlockSrcWithHeader, {mangle: false}).code
|
||||
);
|
||||
|
||||
fs.writeFileSync("content_scripts/dist/instrument.js", builtScriptComment + instrumentSrcWithProxyInjected);
|
||||
|
||||
// Last, several content script files are just copied over, unmodified,
|
||||
// as script files to be injected.
|
||||
const srcFilesToCopy = ["defaults.js"];
|
||||
srcFilesToCopy.forEach(function (aSrcPath) {
|
||||
const scriptSrc = fs.readFileSync("content_scripts/src/" + aSrcPath, "utf8");
|
||||
const scriptSrcWOComments = stripCommentsFromSource(scriptSrc);
|
||||
fs.writeFileSync("content_scripts/dist/" + aSrcPath, builtScriptComment + scriptSrcWOComments);
|
||||
});
|
||||
fs.writeFileSync("data/standards.js", renderedStandardsModule);
|
||||
});
|
||||
|
|
82
lib/cookieencoding.js
Normal file
82
lib/cookieencoding.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*jslint es6: true*/
|
||||
/*global window*/
|
||||
(function () {
|
||||
"use strict";
|
||||
const {packingLib, standards, constants} = window.WEB_API_MANAGER;
|
||||
const standardsNames = Object.keys(standards);
|
||||
const shouldLogKey = constants.shouldLogKey;
|
||||
const allStandardsWithShouldLogOption = standardsNames.concat([shouldLogKey]);
|
||||
|
||||
/**
|
||||
* Creates a cookie safe encoding of standards to block, and
|
||||
* whether logging should be enabled.
|
||||
*
|
||||
* This function is the inverse of the `fromCookieValue` function
|
||||
* in this module.
|
||||
*
|
||||
* The `standardsToBlock` array must be a subset of all the standards
|
||||
* documented in data/standards.
|
||||
*
|
||||
* @param array standardsToBlock
|
||||
* An array of strings, each a standard that should be blocked.
|
||||
* @param bool shouldLog
|
||||
* Whether logging should be enabled.
|
||||
*
|
||||
* @return string
|
||||
* A cookie safe string encoding the above values.
|
||||
*/
|
||||
const toCookieValue = function (standardsToBlock, shouldLog) {
|
||||
|
||||
const standardsToBlockWithshouldLogKey = shouldLog
|
||||
? standardsToBlock.concat(shouldLogKey)
|
||||
: standardsToBlock;
|
||||
|
||||
const packedValues = packingLib.pack(
|
||||
allStandardsWithShouldLogOption,
|
||||
standardsToBlockWithshouldLogKey
|
||||
);
|
||||
|
||||
// Last, replace "=" with "-" in the base64 string, to avoid
|
||||
// silly ambiguities in the cookie value.
|
||||
return packedValues.replace(/\=/g, "-");
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a encoded string (created from the `toCookieValue` function
|
||||
* in this module) and returns to values, one an array of
|
||||
* standard names, and two, a boolean flag of whether the logging option
|
||||
* is enabled.
|
||||
*
|
||||
* @param string data
|
||||
* A string created from `toCookieValue`
|
||||
*
|
||||
* @return [array, bool]
|
||||
* An array of strings of standard names (representing standards to
|
||||
* block), and a boolean describing whether to log blocking
|
||||
* behavior.
|
||||
*/
|
||||
const fromCookieValue = function (data) {
|
||||
|
||||
const base64Data = data.replace(/-/g, "=");
|
||||
|
||||
const unpackedValues = packingLib.unpack(allStandardsWithShouldLogOption, base64Data);
|
||||
|
||||
let shouldLog;
|
||||
const standardsToBlock = unpackedValues;
|
||||
const indexOfShouldLog = unpackedValues.indexOf(shouldLogKey);
|
||||
|
||||
if (indexOfShouldLog === -1) {
|
||||
shouldLog = false;
|
||||
} else {
|
||||
shouldLog = true;
|
||||
standardsToBlock.splice(indexOfShouldLog, 1);
|
||||
}
|
||||
|
||||
return [standardsToBlock, shouldLog];
|
||||
};
|
||||
|
||||
window.WEB_API_MANAGER.cookieEncodingLib = {
|
||||
toCookieValue,
|
||||
fromCookieValue
|
||||
};
|
||||
}());
|
|
@ -1,3 +1,5 @@
|
|||
/*jslint es6: true, browser: true*/
|
||||
/*global window*/
|
||||
/**
|
||||
* This file defines default blocking rules for domains that haven't been
|
||||
* overwritten, either by the extension user, or by a subscribed policy
|
113
lib/httpheaders.js
Normal file
113
lib/httpheaders.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*jslint es6: true*/
|
||||
/*global window*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns a boolean description of whether the given header
|
||||
* (in the structure defined by the WebExtension WebRequest API)
|
||||
* is describing a Set-Cookie instruction.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/HttpHeaders
|
||||
*
|
||||
* @param object header
|
||||
* An object describing a HTTP header
|
||||
*
|
||||
* @return boolean
|
||||
* true if the given object represents a Set-Cookie instruction, and false
|
||||
* in all other cases.
|
||||
*/
|
||||
const isSetCookie = function (header) {
|
||||
|
||||
return (
|
||||
header &&
|
||||
header.name &&
|
||||
header.name.toLowerCase().indexOf("set-cookie") !== -1
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a boolean description of whether the given header
|
||||
* (in the structure defined by the WebExtension WebRequest API)
|
||||
* is describing a Content-Security-Policy for a site.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/HttpHeaders
|
||||
*
|
||||
* @param object header
|
||||
* An object describing a HTTP header
|
||||
*
|
||||
* @return boolean
|
||||
* true if the given object represents a HTTP CSP header, and false
|
||||
* in all other cases.
|
||||
*/
|
||||
const isHeaderCSP = function (header) {
|
||||
|
||||
return (
|
||||
header &&
|
||||
header.name &&
|
||||
header.name.toLowerCase().indexOf("content-security-policy") !== -1
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a boolean description of whether the given header
|
||||
* (in the structure defined by the WebExtension WebRequest API)
|
||||
* is describing a strict dynamic Content-Security-Policy for a site.
|
||||
*
|
||||
* @see https://w3c.github.io/webappsec-csp/#strict-dynamic-usage
|
||||
*
|
||||
* @param object header
|
||||
* An object describing a HTTP header
|
||||
*
|
||||
* @return boolean
|
||||
* true if the given object is a CSP header that defines a
|
||||
* "strict-dynamic" policy, and false in all other cases.
|
||||
*/
|
||||
const isCSPHeaderSettingStrictDynamic = function (header) {
|
||||
|
||||
return (
|
||||
header &&
|
||||
header.name &&
|
||||
header.value.indexOf("'strict-dynamic'") !== -1
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a new CSP instruction, with source with the given hash
|
||||
* whitelisted.
|
||||
*
|
||||
* @see https://w3c.github.io/webappsec-csp/#strict-dynamic-usage
|
||||
* @see https://w3c.github.io/webappsec-csp/#grammardef-hash-source
|
||||
*
|
||||
* @param string cspInstruction
|
||||
* The value of a HTTP header defining a CSP instruction.
|
||||
* @param string scriptHash
|
||||
* A hash value, in the form of "sha256-<some hash>", that is a valid
|
||||
* hash source description.
|
||||
*
|
||||
* @return string|false
|
||||
* Returns false if the CSP instruction looks malformed (ie we
|
||||
* couldn't find a "script-src" tag), otherwise, a new valud
|
||||
* CSP instruction with the given hash allowed.
|
||||
*/
|
||||
const createCSPInstructionWithHashAllowed = function (cspInstruction, scriptHash) {
|
||||
|
||||
const indexOfScriptSrc = cspInstruction.indexOf("script-src ");
|
||||
if (indexOfScriptSrc === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const preSrcScript = cspInstruction.substring(0, indexOfScriptSrc);
|
||||
const postScriptSrc = cspInstruction.substring(indexOfScriptSrc + 10);
|
||||
const newInstruction = preSrcScript + "script-src '" + scriptHash + "' " + postScriptSrc;
|
||||
|
||||
return newInstruction;
|
||||
};
|
||||
|
||||
window.WEB_API_MANAGER.httpHeadersLib = {
|
||||
isSetCookie,
|
||||
isHeaderCSP,
|
||||
isCSPHeaderSettingStrictDynamic,
|
||||
createCSPInstructionWithHashAllowed
|
||||
};
|
||||
}());
|
|
@ -3,5 +3,10 @@
|
|||
// the "namespace" we'll use for all the content scripts in the extension.
|
||||
(function () {
|
||||
"use strict";
|
||||
window.WEB_API_MANAGER = {};
|
||||
window.WEB_API_MANAGER = {
|
||||
constants: {
|
||||
cookieName: "wam-temp-cookie",
|
||||
shouldLogKey: "shouldLogKey"
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
|
236
lib/proxyblock.js
Normal file
236
lib/proxyblock.js
Normal file
|
@ -0,0 +1,236 @@
|
|||
/*jslint es6: true, browser: true*/
|
||||
/*global window, sjcl*/
|
||||
// This module generates JavaScript code for instrumenting the DOM
|
||||
// to prevent pages from accessing Web API standards. This code
|
||||
// is generated programatically so that both the background and content
|
||||
// scripts can determine the SHA256 hash of the injected code, so that
|
||||
// we can set the CSP policy as needed.
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// This function is what does the instrumenting of the DOM,
|
||||
// based on values set in the global window.WEB_API_MANAGER_PAGE
|
||||
// structure. This function is never called, but is rendered to
|
||||
// a string (with Function.prototype.toString) and inserted into
|
||||
// content scripts. Its written here as a proper function
|
||||
// just to make it easier to write and deploy (ie vim highlights
|
||||
// it just like any other JS).
|
||||
const proxyBlockingFunction = function () {
|
||||
console.log("well it runs");
|
||||
const settings = window.WEB_API_MANAGER_PAGE;
|
||||
const shouldLog = settings.shouldLog;
|
||||
const standardsToBlock = settings.toBlock;
|
||||
const standardDefinitions = settings.standards;
|
||||
const hostName = window.location.hostname;
|
||||
|
||||
// Its possible that the Web API removal code will block direct references
|
||||
// to the following methods, so grab references to them before the
|
||||
// DOM is instrumented (and their references are possibly blocked).
|
||||
const removeChild = window.Element.prototype.removeChild;
|
||||
const getElementsByTagName = window.document.getElementsByTagName;
|
||||
|
||||
const defaultFunction = function () {};
|
||||
const funcPropNames = Object.getOwnPropertyNames(defaultFunction);
|
||||
const unconfigurablePropNames = funcPropNames.filter(function (propName) {
|
||||
const possiblePropDesc = Object.getOwnPropertyDescriptor(defaultFunction, propName);
|
||||
return (possiblePropDesc && !possiblePropDesc.configurable);
|
||||
});
|
||||
|
||||
const featuresToBlock = standardsToBlock.reduce(function (prev, cur) {
|
||||
return prev.concat(standardDefinitions[cur].features);
|
||||
}, []);
|
||||
|
||||
const toPrimitiveFunc = function (hint) {
|
||||
if (hint === "number" || hint === "default") {
|
||||
return 0;
|
||||
}
|
||||
if (hint === "string") {
|
||||
return "";
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const keyPathToRefPath = function (keyPath) {
|
||||
const keyParts = keyPath.split(".");
|
||||
return keyParts.reduce(function (prev, cur) {
|
||||
|
||||
if (prev === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const numNodes = prev.length;
|
||||
const currentLeaf = (numNodes === 0)
|
||||
? window
|
||||
: prev[numNodes - 1];
|
||||
const nextLeaf = currentLeaf[cur];
|
||||
|
||||
if (nextLeaf === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return prev.concat([nextLeaf]);
|
||||
}, []);
|
||||
};
|
||||
|
||||
const createBlockingProxy = function (keyPath) {
|
||||
|
||||
let hasBeenLogged = false;
|
||||
|
||||
const logKeyPath = function () {
|
||||
|
||||
if (keyPath !== undefined &&
|
||||
hasBeenLogged === false &&
|
||||
shouldLog) {
|
||||
hasBeenLogged = true;
|
||||
console.log("Blocked '" + keyPath + "' on '" + hostName + "'");
|
||||
}
|
||||
};
|
||||
|
||||
let blockingProxy;
|
||||
blockingProxy = new Proxy(defaultFunction, {
|
||||
get: function (ignore, property) {
|
||||
logKeyPath();
|
||||
|
||||
if (property === Symbol.toPrimitive) {
|
||||
return toPrimitiveFunc;
|
||||
}
|
||||
|
||||
if (property === "valueOf") {
|
||||
return toPrimitiveFunc;
|
||||
}
|
||||
|
||||
return blockingProxy;
|
||||
},
|
||||
set: function () {
|
||||
logKeyPath();
|
||||
return blockingProxy;
|
||||
},
|
||||
apply: function () {
|
||||
logKeyPath();
|
||||
return blockingProxy;
|
||||
},
|
||||
ownKeys: function (ignore) {
|
||||
return unconfigurablePropNames;
|
||||
},
|
||||
has: function (ignore, property) {
|
||||
return (unconfigurablePropNames.indexOf(property) > -1);
|
||||
},
|
||||
getOwnPropertyDescriptor: function (ignore, property) {
|
||||
if (unconfigurablePropNames.indexOf(property) === -1) {
|
||||
return undefined;
|
||||
}
|
||||
return Object.getOwnPropertyDescriptor(defaultFunction, property);
|
||||
}
|
||||
});
|
||||
|
||||
return blockingProxy;
|
||||
};
|
||||
|
||||
const defaultBlockingProxy = createBlockingProxy();
|
||||
|
||||
const blockFeatureAtKeyPath = function (keyPath) {
|
||||
const propertyRefs = keyPathToRefPath(keyPath);
|
||||
|
||||
// If we weren't able to turn the key path into an array of references,
|
||||
// then it means that the property doesn't exist in this DOM /
|
||||
// environment, so there is nothing to block.
|
||||
if (propertyRefs === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const keyPathSegments = keyPath.split(".");
|
||||
const lastPropertyName = keyPathSegments[keyPathSegments.length - 1];
|
||||
const leafRef = propertyRefs[propertyRefs.length - 1];
|
||||
const parentRef = propertyRefs[propertyRefs.length - 2];
|
||||
|
||||
// At least for now, only interpose on methods.
|
||||
if (typeof leafRef !== "function") {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (shouldLog === true) {
|
||||
parentRef[lastPropertyName] = createBlockingProxy(keyPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
parentRef[lastPropertyName] = defaultBlockingProxy;
|
||||
return true;
|
||||
|
||||
} catch (e) {
|
||||
|
||||
if (shouldLog) {
|
||||
console.log("Error instrumenting " + keyPath + ": " + e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
featuresToBlock.forEach(blockFeatureAtKeyPath);
|
||||
|
||||
// Next, delete the WEB_API_MANAGER_PAGE global property. Technically
|
||||
// this never needed to be global, but doing so allows for easier
|
||||
// jslinting of the code, makes things easier to understand (for me
|
||||
// at least) and doesn't have any side effect as long as we delete
|
||||
// it when we're done, and before the page scripts can start running.
|
||||
delete window.WEB_API_MANAGER_PAGE;
|
||||
|
||||
console.log("well its done");
|
||||
// Last, remove the script tag containing this code from the document,
|
||||
// so that the structure of the page looks like what the page author
|
||||
// expects / intended.
|
||||
const scriptTags = getElementsByTagName.call(window.document, "script");
|
||||
removeChild.call(scriptTags[0].parentNode, scriptTags[0]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a script payload, for injecting into content scripts. The
|
||||
* generated string is 99% the above proxyBlockingFunction function,
|
||||
* but with the window.WEB_API_MANAGER_PAGE object set up
|
||||
* correctly to block the desired functions.
|
||||
*
|
||||
* @param object standards
|
||||
* A mapping of standard names to information about those standards.
|
||||
* The structure of this object should match whats in data/standards.js
|
||||
* @param array standardNamesToBlock
|
||||
* An array of strings, which must be a subset of the keys of the
|
||||
* standards object.
|
||||
* @param bool shouldLog
|
||||
* Whether to log the behavior of the blocking proxy.
|
||||
*
|
||||
* @return [string, hash]
|
||||
* Returns an array containing two values. First, JavaScript code
|
||||
* that instruments the DOM of page's its injected into to render the
|
||||
* standardNamesToBlock standards un-reachable, and second, a
|
||||
* base64 encoded sha256 hash of the code.
|
||||
*/
|
||||
const generateScriptPayload = function (standards, standardNamesToBlock, shouldLog) {
|
||||
|
||||
const proxyBlockingSettings = `
|
||||
window.WEB_API_MANAGER_PAGE = {
|
||||
standards: ${JSON.stringify(standards)},
|
||||
toBlock: ${JSON.stringify(standardNamesToBlock)},
|
||||
shouldLog: ${shouldLog ? "true" : "false"}
|
||||
};
|
||||
`;
|
||||
|
||||
const proxyingBlockingSrc = "(" + proxyBlockingFunction.toString() + "())";
|
||||
const completeScriptCode = proxyBlockingSettings + "\n" + proxyingBlockingSrc;
|
||||
|
||||
// Use the SJ Crypto library, instead of the WebCrypto library,
|
||||
// because we need to compute hashes syncronously (so we can
|
||||
// be sure the hash operation will complete before we let page
|
||||
// script run).
|
||||
// https://bitwiseshiftleft.github.io/sjcl/doc/sjcl.hash.sha256.html
|
||||
const hash = sjcl.hash.sha256.hash(completeScriptCode);
|
||||
const hashBase64 = sjcl.codec.base64.fromBits(hash);
|
||||
|
||||
return [completeScriptCode, hashBase64];
|
||||
};
|
||||
|
||||
window.WEB_API_MANAGER.proxyBlockLib = {
|
||||
generateScriptPayload
|
||||
};
|
||||
}());
|
0
lib/URI.js → lib/vendor/URI.js
vendored
0
lib/URI.js → lib/vendor/URI.js
vendored
0
lib/js.cookie.js → lib/vendor/js.cookie.js
vendored
0
lib/js.cookie.js → lib/vendor/js.cookie.js
vendored
60
lib/vendor/sjcl.js
vendored
Normal file
60
lib/vendor/sjcl.js
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
|
||||
sjcl.cipher.aes=function(a){this.s[0][0][0]||this.O();var b,c,d,e,f=this.s[0][4],g=this.s[1];b=a.length;var h=1;if(4!==b&&6!==b&&8!==b)throw new sjcl.exception.invalid("invalid aes key size");this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&
|
||||
255]]};
|
||||
sjcl.cipher.aes.prototype={encrypt:function(a){return t(this,a,0)},decrypt:function(a){return t(this,a,1)},s:[[[],[],[],[],[]],[[],[],[],[],[]]],O:function(){var a=this.s[0],b=this.s[1],c=a[4],d=b[4],e,f,g,h=[],k=[],l,n,m,p;for(e=0;0x100>e;e++)k[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=l||1,g=k[g]||1)for(m=g^g<<1^g<<2^g<<3^g<<4,m=m>>8^m&255^99,c[f]=m,d[m]=f,n=h[e=h[l=h[f]]],p=0x1010101*n^0x10001*e^0x101*l^0x1010100*f,n=0x101*h[m]^0x1010100*m,e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8;for(e=
|
||||
0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}};
|
||||
function t(a,b,c){if(4!==b.length)throw new sjcl.exception.invalid("invalid aes block size");var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,k,l,n=d.length/4-2,m,p=4,r=[0,0,0,0];h=a.s[c];a=h[0];var q=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m<n;m++)h=a[e>>>24]^q[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],k=a[f>>>24]^q[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],l=a[g>>>24]^q[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^q[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=k,g=l;for(m=
|
||||
0;4>m;m++)r[c?3&-m:m]=x[e>>>24]<<24^x[f>>16&255]<<16^x[g>>8&255]<<8^x[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return r}
|
||||
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.$(a.slice(b/32),32-(b&31)).slice(1);return void 0===c?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.$(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
|
||||
b?0:32*(b-1)+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(32*a.length<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b=b&31;0<c&&b&&(a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return!1;var c=0,d;for(d=0;d<a.length;d++)c|=a[d]^b[d];return 0===
|
||||
c},$:function(a,b,c,d){var e;e=0;for(void 0===d&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},i:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]},byteswapM:function(a){var b,c;for(b=0;b<a.length;++b)c=a[b],a[b]=c>>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}};
|
||||
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++)0===(d&3)&&(e=a[d/4]),b+=String.fromCharCode(e>>>8>>>8>>>8),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++)d=d<<8|a.charCodeAt(c),3===(c&3)&&(b.push(d),d=0);c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
|
||||
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a=a+"00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,4*d)}};
|
||||
sjcl.codec.base32={B:"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",X:"0123456789ABCDEFGHIJKLMNOPQRSTUV",BITS:32,BASE:5,REMAINING:27,fromBits:function(a,b,c){var d=sjcl.codec.base32.BASE,e=sjcl.codec.base32.REMAINING,f="",g=0,h=sjcl.codec.base32.B,k=0,l=sjcl.bitArray.bitLength(a);c&&(h=sjcl.codec.base32.X);for(c=0;f.length*d<l;)f+=h.charAt((k^a[c]>>>g)>>>e),g<d?(k=a[c]<<d-g,g+=e,c++):(k<<=d,g-=d);for(;f.length&7&&!b;)f+="=";return f},toBits:function(a,b){a=a.replace(/\s|=/g,"").toUpperCase();var c=sjcl.codec.base32.BITS,
|
||||
d=sjcl.codec.base32.BASE,e=sjcl.codec.base32.REMAINING,f=[],g,h=0,k=sjcl.codec.base32.B,l=0,n,m="base32";b&&(k=sjcl.codec.base32.X,m="base32hex");for(g=0;g<a.length;g++){n=k.indexOf(a.charAt(g));if(0>n){if(!b)try{return sjcl.codec.base32hex.toBits(a)}catch(p){}throw new sjcl.exception.invalid("this isn't "+m+"!");}h>e?(h-=e,f.push(l^n>>>h),l=n<<c-h):(h+=d,l^=n<<c-h)}h&56&&f.push(sjcl.bitArray.partial(h&56,l,1));return f}};
|
||||
sjcl.codec.base32hex={fromBits:function(a,b){return sjcl.codec.base32.fromBits(a,b,1)},toBits:function(a){return sjcl.codec.base32.toBits(a,1)}};
|
||||
sjcl.codec.base64={B:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.B,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.B,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;d<a.length;d++){h=f.indexOf(a.charAt(d));
|
||||
if(0>h)throw new sjcl.exception.invalid("this isn't base64!");26<e?(e-=26,c.push(g^h>>>e),g=h<<32-e):(e+=6,g^=h<<32-e)}e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.O();a?(this.F=a.F.slice(0),this.A=a.A.slice(0),this.l=a.l):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
|
||||
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.F=this.Y.slice(0);this.A=[];this.l=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.A=sjcl.bitArray.concat(this.A,a);b=this.l;a=this.l=b+sjcl.bitArray.bitLength(a);if(0x1fffffffffffff<a)throw new sjcl.exception.invalid("Cannot hash more than 2^53 - 1 bits");if("undefined"!==typeof Uint32Array){var d=new Uint32Array(c),e=0;for(b=512+b-(512+b&0x1ff);b<=a;b+=512)u(this,d.subarray(16*e,
|
||||
16*(e+1))),e+=1;c.splice(0,16*e)}else for(b=512+b-(512+b&0x1ff);b<=a;b+=512)u(this,c.splice(0,16));return this},finalize:function(){var a,b=this.A,c=this.F,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.l/0x100000000));for(b.push(this.l|0);b.length;)u(this,b.splice(0,16));this.reset();return c},Y:[],b:[],O:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}for(var b=0,c=2,d,e;64>b;c++){e=!0;for(d=2;d*d<=c;d++)if(0===c%d){e=
|
||||
!1;break}e&&(8>b&&(this.Y[b]=a(Math.pow(c,.5))),this.b[b]=a(Math.pow(c,1/3)),b++)}}};
|
||||
function u(a,b){var c,d,e,f=a.F,g=a.b,h=f[0],k=f[1],l=f[2],n=f[3],m=f[4],p=f[5],r=f[6],q=f[7];for(c=0;64>c;c++)16>c?d=b[c]:(d=b[c+1&15],e=b[c+14&15],d=b[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+b[c&15]+b[c+9&15]|0),d=d+q+(m>>>6^m>>>11^m>>>25^m<<26^m<<21^m<<7)+(r^m&(p^r))+g[c],q=r,r=p,p=m,m=n+d|0,n=l,l=k,k=h,h=d+(k&l^n&(k^l))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;f[0]=f[0]+h|0;f[1]=f[1]+k|0;f[2]=f[2]+l|0;f[3]=f[3]+n|0;f[4]=f[4]+m|0;f[5]=f[5]+p|0;f[6]=f[6]+r|0;f[7]=
|
||||
f[7]+q|0}
|
||||
sjcl.mode.ccm={name:"ccm",G:[],listenProgress:function(a){sjcl.mode.ccm.G.push(a)},unListenProgress:function(a){a=sjcl.mode.ccm.G.indexOf(a);-1<a&&sjcl.mode.ccm.G.splice(a,1)},fa:function(a){var b=sjcl.mode.ccm.G.slice(),c;for(c=0;c<b.length;c+=1)b[c](a)},encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,k=h.bitLength(c)/8,l=h.bitLength(g)/8;e=e||64;d=d||[];if(7>k)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;4>f&&l>>>8*f;f++);f<15-k&&(f=15-k);c=h.clamp(c,
|
||||
8*(15-f));b=sjcl.mode.ccm.V(a,b,c,d,e,f);g=sjcl.mode.ccm.C(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),k=f.clamp(b,h-e),l=f.bitSlice(b,h-e),h=(h-e)/8;if(7>g)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));k=sjcl.mode.ccm.C(a,k,c,l,e,b);a=sjcl.mode.ccm.V(a,k.data,c,d,e,b);if(!f.equal(k.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match");
|
||||
return k.data},na:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,k=h.i;d=[h.partial(8,(b.length?64:0)|d-2<<2|f-1)];d=h.concat(d,c);d[3]|=e;d=a.encrypt(d);if(b.length)for(c=h.bitLength(b)/8,65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c])),g=h.concat(g,b),b=0;b<g.length;b+=4)d=a.encrypt(k(d,g.slice(b,b+4).concat([0,0,0])));return d},V:function(a,b,c,d,e,f){var g=sjcl.bitArray,h=g.i;e/=8;if(e%2||4>e||16<e)throw new sjcl.exception.invalid("ccm: invalid tag length");
|
||||
if(0xffffffff<d.length||0xffffffff<b.length)throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");c=sjcl.mode.ccm.na(a,d,c,e,g.bitLength(b)/8,f);for(d=0;d<b.length;d+=4)c=a.encrypt(h(c,b.slice(d,d+4).concat([0,0,0])));return g.clamp(c,8*e)},C:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.i;var k=b.length,l=h.bitLength(b),n=k/50,m=n;c=h.concat([h.partial(8,f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!k)return{tag:d,data:[]};for(g=0;g<k;g+=4)g>n&&(sjcl.mode.ccm.fa(g/
|
||||
k),n+=m),c[3]++,e=a.encrypt(c),b[g]^=e[0],b[g+1]^=e[1],b[g+2]^=e[2],b[g+3]^=e[3];return{tag:d,data:h.clamp(b,l)}}};
|
||||
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){if(128!==sjcl.bitArray.bitLength(c))throw new sjcl.exception.invalid("ocb iv must be 128 bits");var g,h=sjcl.mode.ocb2.S,k=sjcl.bitArray,l=k.i,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4)m=b.slice(g,g+4),n=l(n,m),p=p.concat(l(c,a.encrypt(l(c,m)))),c=h(c);m=b.slice(g);b=k.bitLength(m);g=a.encrypt(l(c,[0,0,0,b]));m=k.clamp(l(m.concat([0,0,0]),g),b);n=l(n,l(m.concat([0,0,0]),g));n=a.encrypt(l(n,l(c,h(c))));
|
||||
d.length&&(n=l(n,f?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(k.concat(m,k.clamp(n,e)))},decrypt:function(a,b,c,d,e,f){if(128!==sjcl.bitArray.bitLength(c))throw new sjcl.exception.invalid("ocb iv must be 128 bits");e=e||64;var g=sjcl.mode.ocb2.S,h=sjcl.bitArray,k=h.i,l=[0,0,0,0],n=g(a.encrypt(c)),m,p,r=sjcl.bitArray.bitLength(b)-e,q=[];d=d||[];for(c=0;c+4<r/32;c+=4)m=k(n,a.decrypt(k(n,b.slice(c,c+4)))),l=k(l,m),q=q.concat(m),n=g(n);p=r-32*c;m=a.encrypt(k(n,[0,0,0,p]));m=k(m,h.clamp(b.slice(c),p).concat([0,
|
||||
0,0]));l=k(l,m);l=a.encrypt(k(l,k(n,g(n))));d.length&&(l=k(l,f?d:sjcl.mode.ocb2.pmac(a,d)));if(!h.equal(h.clamp(l,e),h.bitSlice(b,r)))throw new sjcl.exception.corrupt("ocb: tag doesn't match");return q.concat(h.clamp(m,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.S,e=sjcl.bitArray,f=e.i,g=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),g=f(g,a.encrypt(f(h,b.slice(c,c+4))));c=b.slice(c);128>e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);
|
||||
return a.encrypt(f(d(f(h,d(h))),g))},S:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};
|
||||
sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.C(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.C(!1,a,f,d,c,e);if(!g.equal(a.tag,b))throw new sjcl.exception.corrupt("gcm: tag doesn't match");return a.data},ka:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.i;e=[0,0,
|
||||
0,0];f=b.slice(0);for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0<d;d--)f[d]=f[d]>>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},j:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.ka(b,a);return b},C:function(a,b,c,d,e,f){var g,h,k,l,n,m,p,r,q=sjcl.bitArray;m=c.length;p=q.bitLength(c);r=q.bitLength(d);h=q.bitLength(e);
|
||||
g=b.encrypt([0,0,0,0]);96===h?(e=e.slice(0),e=q.concat(e,[1])):(e=sjcl.mode.gcm.j(g,[0,0,0,0],e),e=sjcl.mode.gcm.j(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.j(g,[0,0,0,0],d);n=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.j(g,h,c));for(l=0;l<m;l+=4)n[3]++,k=b.encrypt(n),c[l]^=k[0],c[l+1]^=k[1],c[l+2]^=k[2],c[l+3]^=k[3];c=q.clamp(c,p);a&&(d=sjcl.mode.gcm.j(g,h,c));a=[Math.floor(r/0x100000000),r&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.j(g,d,a);k=b.encrypt(e);
|
||||
d[0]^=k[0];d[1]^=k[1];d[2]^=k[2];d[3]^=k[3];return{tag:q.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.W=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.w=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.w[0].update(c[0]);this.w[1].update(c[1]);this.R=new b(this.w[0])};
|
||||
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){if(this.aa)throw new sjcl.exception.invalid("encrypt on already updated hmac called!");this.update(a);return this.digest(a)};sjcl.misc.hmac.prototype.reset=function(){this.R=new this.W(this.w[0]);this.aa=!1};sjcl.misc.hmac.prototype.update=function(a){this.aa=!0;this.R.update(a)};sjcl.misc.hmac.prototype.digest=function(){var a=this.R.finalize(),a=(new this.W(this.w[1])).update(a).finalize();this.reset();return a};
|
||||
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E4;if(0>d||0>c)throw new sjcl.exception.invalid("invalid params to pbkdf2");"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,k,l=[],n=sjcl.bitArray;for(k=1;32*l.length<(d||1);k++){e=f=a.encrypt(n.concat(b,[k]));for(g=1;g<c;g++)for(f=a.encrypt(f),h=0;h<f.length;h++)e[h]^=f[h];l=l.concat(e)}d&&(l=n.clamp(l,d));return l};
|
||||
sjcl.prng=function(a){this.c=[new sjcl.hash.sha256];this.m=[0];this.P=0;this.H={};this.N=0;this.U={};this.Z=this.f=this.o=this.ha=0;this.b=[0,0,0,0,0,0,0,0];this.h=[0,0,0,0];this.L=void 0;this.M=a;this.D=!1;this.K={progress:{},seeded:{}};this.u=this.ga=0;this.I=1;this.J=2;this.ca=0x10000;this.T=[0,48,64,96,128,192,0x100,384,512,768,1024];this.da=3E4;this.ba=80};
|
||||
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;if(d===this.u)throw new sjcl.exception.notReady("generator isn't seeded");if(d&this.J){d=!(d&this.I);e=[];var f=0,g;this.Z=e[0]=(new Date).valueOf()+this.da;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.c.length&&(e=e.concat(this.c[g].finalize()),f+=this.m[g],this.m[g]=0,d||!(this.P&1<<g));g++);this.P>=1<<this.c.length&&(this.c.push(new sjcl.hash.sha256),this.m.push(0));this.f-=f;f>this.o&&(this.o=
|
||||
f);this.P++;this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.L=new sjcl.cipher.aes(this.b);for(d=0;4>d&&(this.h[d]=this.h[d]+1|0,!this.h[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.ca&&y(this),e=z(this),c.push(e[0],e[1],e[2],e[3]);y(this);return c.slice(0,a)},setDefaultParanoia:function(a,b){if(0===a&&"Setting paranoia=0 will ruin your security; use it only for testing"!==b)throw new sjcl.exception.invalid("Setting paranoia=0 will ruin your security; use it only for testing");this.M=a},addEntropy:function(a,
|
||||
b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.H[c],h=this.isReady(),k=0;d=this.U[c];void 0===d&&(d=this.U[c]=this.ha++);void 0===g&&(g=this.H[c]=0);this.H[c]=(this.H[c]+1)%this.c.length;switch(typeof a){case "number":void 0===b&&(b=1);this.c[g].update([d,this.N++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else for("[object Array]"!==c&&(k=1),c=0;c<a.length&&!k;c++)"number"!==typeof a[c]&&
|
||||
(k=1);if(!k){if(void 0===b)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,e=e>>>1;this.c[g].update([d,this.N++,2,b,f,a.length].concat(a))}break;case "string":void 0===b&&(b=a.length);this.c[g].update([d,this.N++,3,b,f,a.length]);this.c[g].update(a);break;default:k=1}if(k)throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");this.m[g]+=b;this.f+=b;h===this.u&&(this.isReady()!==this.u&&A("seeded",Math.max(this.o,this.f)),A("progress",this.getProgress()))},
|
||||
isReady:function(a){a=this.T[void 0!==a?a:this.M];return this.o&&this.o>=a?this.m[0]>this.ba&&(new Date).valueOf()>this.Z?this.J|this.I:this.I:this.f>=a?this.J|this.u:this.u},getProgress:function(a){a=this.T[a?a:this.M];return this.o>=a?1:this.f>a?1:this.f/a},startCollectors:function(){if(!this.D){this.a={loadTimeCollector:B(this,this.ma),mouseCollector:B(this,this.oa),keyboardCollector:B(this,this.la),accelerometerCollector:B(this,this.ea),touchCollector:B(this,this.qa)};if(window.addEventListener)window.addEventListener("load",
|
||||
this.a.loadTimeCollector,!1),window.addEventListener("mousemove",this.a.mouseCollector,!1),window.addEventListener("keypress",this.a.keyboardCollector,!1),window.addEventListener("devicemotion",this.a.accelerometerCollector,!1),window.addEventListener("touchmove",this.a.touchCollector,!1);else if(document.attachEvent)document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector);else throw new sjcl.exception.bug("can't attach event");
|
||||
this.D=!0}},stopCollectors:function(){this.D&&(window.removeEventListener?(window.removeEventListener("load",this.a.loadTimeCollector,!1),window.removeEventListener("mousemove",this.a.mouseCollector,!1),window.removeEventListener("keypress",this.a.keyboardCollector,!1),window.removeEventListener("devicemotion",this.a.accelerometerCollector,!1),window.removeEventListener("touchmove",this.a.touchCollector,!1)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove",
|
||||
this.a.mouseCollector),document.detachEvent("keypress",this.a.keyboardCollector)),this.D=!1)},addEventListener:function(a,b){this.K[a][this.ga++]=b},removeEventListener:function(a,b){var c,d,e=this.K[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;c<f.length;c++)d=f[c],delete e[d]},la:function(){C(this,1)},oa:function(a){var b,c;try{b=a.x||a.clientX||a.offsetX||0,c=a.y||a.clientY||a.offsetY||0}catch(d){c=b=0}0!=b&&0!=c&&this.addEntropy([b,c],2,"mouse");C(this,0)},qa:function(a){a=
|
||||
a.touches[0]||a.changedTouches[0];this.addEntropy([a.pageX||a.clientX,a.pageY||a.clientY],1,"touch");C(this,0)},ma:function(){C(this,2)},ea:function(a){a=a.accelerationIncludingGravity.x||a.accelerationIncludingGravity.y||a.accelerationIncludingGravity.z;if(window.orientation){var b=window.orientation;"number"===typeof b&&this.addEntropy(b,1,"accelerometer")}a&&this.addEntropy(a,2,"accelerometer");C(this,0)}};
|
||||
function A(a,b){var c,d=sjcl.random.K[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}function C(a,b){"undefined"!==typeof window&&window.performance&&"function"===typeof window.performance.now?a.addEntropy(window.performance.now(),b,"loadtime"):a.addEntropy((new Date).valueOf(),b,"loadtime")}function y(a){a.b=z(a).concat(z(a));a.L=new sjcl.cipher.aes(a.b)}function z(a){for(var b=0;4>b&&(a.h[b]=a.h[b]+1|0,!a.h[b]);b++);return a.L.encrypt(a.h)}
|
||||
function B(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6);
|
||||
a:try{var D,E,F,G;if(G="undefined"!==typeof module&&module.exports){var H;try{H=require("crypto")}catch(a){H=null}G=E=H}if(G&&E.randomBytes)D=E.randomBytes(128),D=new Uint32Array((new Uint8Array(D)).buffer),sjcl.random.addEntropy(D,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){F=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(F);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(F);
|
||||
else break a;sjcl.random.addEntropy(F,1024,"crypto['getRandomValues']")}}catch(a){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(a))}
|
||||
sjcl.json={defaults:{v:1,iter:1E4,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},ja:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.g({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.g(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));if(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||
|
||||
4<f.iv.length)throw new sjcl.exception.invalid("json encrypt: invalid parameters");"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(f.adata=c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.g(d,f);d.key=a;f.ct="ccm"===f.mode&&sjcl.arrayBuffer&&sjcl.arrayBuffer.ccm&&
|
||||
b instanceof ArrayBuffer?sjcl.arrayBuffer.ccm.encrypt(g,b,f.iv,c,f.ts):sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return f},encrypt:function(a,b,c,d){var e=sjcl.json,f=e.ja.apply(e,arguments);return e.encode(f)},ia:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.g(e.g(e.g({},e.defaults),b),c,!0);var f,g;f=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===
|
||||
typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)throw new sjcl.exception.invalid("json decrypt: invalid parameters");"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,b),a=g.key.slice(0,b.ks/32),b.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof f&&(f=sjcl.codec.utf8String.toBits(f));g=new sjcl.cipher[b.cipher](a);f="ccm"===
|
||||
b.mode&&sjcl.arrayBuffer&&sjcl.arrayBuffer.ccm&&b.ct instanceof ArrayBuffer?sjcl.arrayBuffer.ccm.decrypt(g,b.ct,b.iv,b.tag,f,b.ts):sjcl.mode[b.mode].decrypt(g,b.ct,b.iv,f,b.ts);e.g(d,b);d.key=a;return 1===c.raw?f:sjcl.codec.utf8String.fromBits(f)},decrypt:function(a,b,c,d){var e=sjcl.json;return e.ia(a,e.decode(b),c,d)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+'"'+
|
||||
b+'":';d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:throw new sjcl.exception.bug("json encode: unsupported type");}}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");if(!a.match(/^\{.*\}$/))throw new sjcl.exception.invalid("json decode: this isn't json!");a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++){if(!(d=a[c].match(/^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i)))throw new sjcl.exception.invalid("json decode: this isn't json!");
|
||||
null!=d[3]?b[d[2]]=parseInt(d[3],10):null!=d[4]?b[d[2]]=d[2].match(/^(ct|adata|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]):null!=d[5]&&(b[d[2]]="true"===d[5])}return b},g:function(a,b,c){void 0===a&&(a={});if(void 0===b)return a;for(var d in b)if(b.hasOwnProperty(d)){if(c&&void 0!==a[d]&&a[d]!==b[d])throw new sjcl.exception.invalid("required parameter overridden");a[d]=b[d]}return a},sa:function(a,b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},ra:function(a,
|
||||
b){var c={},d;for(d=0;d<b.length;d++)void 0!==a[b[d]]&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.pa={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.pa,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=void 0===b.salt?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
|
||||
"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);"function"===typeof define&&define([],function(){return sjcl});
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "WebAPI Manager",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"description": "Improves browser security and privacy by controlling page access to the Web API.",
|
||||
"icons": {
|
||||
"48": "images/uic-48.png",
|
||||
|
@ -31,32 +31,32 @@
|
|||
{
|
||||
"matches": ["*://*/*"],
|
||||
"js": [
|
||||
"lib/vendor/js.cookie.js",
|
||||
"lib/vendor/sjcl.js",
|
||||
"lib/init.js",
|
||||
"data/standards.js",
|
||||
"lib/pack.js",
|
||||
"lib/js.cookie.js",
|
||||
"content_scripts/dist/standards.js",
|
||||
"content_scripts/dist/instrument.js"
|
||||
"lib/cookieencoding.js",
|
||||
"lib/proxyblock.js",
|
||||
"content_scripts/instrument.js"
|
||||
],
|
||||
"all_frames": true,
|
||||
"run_at": "document_start"
|
||||
}
|
||||
],
|
||||
"web_accessible_resources": [
|
||||
"lib/init.js",
|
||||
"lib/js.cookie.js",
|
||||
"lib/storage.js",
|
||||
"lib/URI.js",
|
||||
"content_scripts/dist/defaults.js"
|
||||
],
|
||||
"background": {
|
||||
"scripts": [
|
||||
"lib/vendor/sjcl.js",
|
||||
"lib/vendor/URI.js",
|
||||
"lib/init.js",
|
||||
"data/standards.js",
|
||||
"lib/pack.js",
|
||||
"lib/URI.js",
|
||||
"content_scripts/dist/standards.js",
|
||||
"content_scripts/dist/defaults.js",
|
||||
"lib/defaults.js",
|
||||
"lib/storage.js",
|
||||
"lib/domainmatcher.js",
|
||||
"lib/cookieencoding.js",
|
||||
"lib/proxyblock.js",
|
||||
"lib/httpheaders.js",
|
||||
"background_scripts/background.js"
|
||||
]
|
||||
},
|
||||
|
|
400
package-lock.json
generated
400
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -5,8 +5,7 @@
|
|||
"author": "Peter Snyder <psnyde2@uic.edu> (https://www.cs.uic.edu/~psnyder/)",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"gulp": "^3.9.1",
|
||||
"uglify-es": "^3.1.3"
|
||||
"gulp": "^3.9.1"
|
||||
},
|
||||
"homepage": "https://github.com/snyderp/web-api-manager",
|
||||
"bugs": {
|
||||
|
|
Loading…
Reference in a new issue