2016-02-13 12:28:36 +01:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
(function(){
|
|
|
|
"use strict";
|
|
|
|
|
2017-06-25 22:22:17 +02:00
|
|
|
var scope;
|
|
|
|
if ((typeof exports) !== "undefined"){
|
|
|
|
scope = exports;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
window.scope.intercept = {};
|
|
|
|
scope = window.scope.intercept;
|
|
|
|
}
|
|
|
|
|
2018-08-27 00:23:19 +02:00
|
|
|
const {changedFunctions, changedGetters, setRandomSupply} = require("./modifiedAPI");
|
2016-08-06 19:17:36 +02:00
|
|
|
const randomSupplies = require("./randomSupplies");
|
2017-10-07 23:26:04 +02:00
|
|
|
const getWrapped = require("sdk/getWrapped");
|
2017-11-07 00:36:44 +01:00
|
|
|
const logging = require("./logging");
|
2017-12-04 00:26:26 +01:00
|
|
|
const settings = require("./settings");
|
2017-10-07 23:26:04 +02:00
|
|
|
|
2016-08-06 19:17:36 +02:00
|
|
|
setRandomSupply(randomSupplies.nonPersistent);
|
2016-02-13 12:28:36 +01:00
|
|
|
var apiNames = Object.keys(changedFunctions);
|
|
|
|
var undef;
|
2016-08-06 19:17:36 +02:00
|
|
|
function setRandomSupplyByType(type){
|
|
|
|
switch (type){
|
|
|
|
case "persistent":
|
|
|
|
setRandomSupply(randomSupplies.persistent);
|
|
|
|
break;
|
2017-08-07 17:43:57 +02:00
|
|
|
case "constant":
|
|
|
|
setRandomSupply(randomSupplies.constant);
|
|
|
|
break;
|
2017-11-24 17:06:43 +01:00
|
|
|
case "white":
|
|
|
|
setRandomSupply(randomSupplies.white);
|
|
|
|
break;
|
2016-08-06 19:17:36 +02:00
|
|
|
default:
|
|
|
|
setRandomSupply(randomSupplies.nonPersistent);
|
|
|
|
}
|
|
|
|
}
|
2017-06-25 22:22:17 +02:00
|
|
|
scope.setRandomSupplyByType = setRandomSupplyByType;
|
2017-01-31 20:00:02 +01:00
|
|
|
|
|
|
|
function getURL(window){
|
2017-07-18 16:14:03 +02:00
|
|
|
var href;
|
|
|
|
try {
|
|
|
|
href = window.location.href;
|
|
|
|
}
|
|
|
|
catch (e){
|
|
|
|
// unable to read location due to SOP
|
|
|
|
// since we are not able to do anything in that case we can allow everything
|
|
|
|
return "about:SOP";
|
|
|
|
}
|
|
|
|
if (!href || href === "about:blank"){
|
2017-01-31 20:00:02 +01:00
|
|
|
if (window !== window.parent){
|
|
|
|
return getURL(window.parent);
|
|
|
|
}
|
|
|
|
else if (window.opener){
|
|
|
|
return getURL(window.opener);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return window.location.href;
|
|
|
|
}
|
|
|
|
|
2017-12-04 00:26:26 +01:00
|
|
|
scope.preIntercept = function preIntercept({subject: window}, apis){
|
|
|
|
if (!settings.isStillDefault){
|
2017-12-18 23:55:13 +01:00
|
|
|
logging.message("settings already loaded -> no need to pre intercept");
|
2017-12-04 00:26:26 +01:00
|
|
|
scope.intercept({subject: window}, apis);
|
|
|
|
}
|
|
|
|
else {
|
2018-08-22 22:16:49 +02:00
|
|
|
logging.message("settings not loaded -> need to pre intercept");
|
2017-12-15 01:00:52 +01:00
|
|
|
let forceLoad = true;
|
2017-12-04 00:26:26 +01:00
|
|
|
let preIntercepted = false;
|
|
|
|
let intercepted = false;
|
|
|
|
const forEachFunction = function(callback){
|
|
|
|
apiNames.forEach(function(name){
|
|
|
|
const changedFunction = changedFunctions[name];
|
|
|
|
(
|
|
|
|
Array.isArray(changedFunction.object)?
|
|
|
|
changedFunction.object:
|
|
|
|
[changedFunction.object]
|
|
|
|
).forEach(function(object){
|
|
|
|
var constructor = getWrapped(window)[object];
|
|
|
|
if (constructor){
|
2018-08-27 00:23:19 +02:00
|
|
|
callback({name, object: constructor.prototype});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
changedGetters.forEach(function(changedGetter){
|
|
|
|
const name = changedGetter.name;
|
|
|
|
changedGetter.objectGetters.forEach(function(objectGetter){
|
|
|
|
const object = objectGetter(getWrapped(window));
|
|
|
|
if (object){
|
|
|
|
callback({name, object});
|
2017-12-04 00:26:26 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
let originalPropertyDescriptors = {};
|
|
|
|
const doPreIntercept = function(){
|
|
|
|
if (!preIntercepted){
|
2018-08-27 00:23:19 +02:00
|
|
|
forEachFunction(function({name, object}){
|
2017-12-04 00:26:26 +01:00
|
|
|
var map = originalPropertyDescriptors[name] || new WeakMap();
|
|
|
|
originalPropertyDescriptors[name] = map;
|
2018-09-05 15:50:59 +02:00
|
|
|
|
|
|
|
const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(object, name);
|
|
|
|
if (!originalPropertyDescriptor){
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
map.set(object, originalPropertyDescriptor);
|
2017-12-04 00:26:26 +01:00
|
|
|
Object.defineProperty(
|
2018-08-27 00:23:19 +02:00
|
|
|
object,
|
2017-12-04 00:26:26 +01:00
|
|
|
name,
|
|
|
|
{
|
|
|
|
enumerable: true,
|
2018-08-22 22:16:49 +02:00
|
|
|
configurable: true,
|
2017-12-04 00:26:26 +01:00
|
|
|
get: exportFunction(function(){
|
2017-12-13 20:15:50 +01:00
|
|
|
if (forceLoad){
|
2017-12-18 23:55:13 +01:00
|
|
|
logging.warning("force load the settings. Calling stack:", (new Error()).stack);
|
2017-12-13 20:15:50 +01:00
|
|
|
undoPreIntercept();
|
|
|
|
settings.forceLoad();
|
|
|
|
doRealIntercept();
|
2018-08-27 00:23:19 +02:00
|
|
|
var descriptor = Object.getOwnPropertyDescriptor(object, name);
|
|
|
|
return descriptor.value || descriptor.get.call(this);
|
2017-12-13 20:15:50 +01:00
|
|
|
}
|
|
|
|
else {
|
2017-12-18 23:55:13 +01:00
|
|
|
logging.notice("API blocked (%s)", name);
|
2017-12-13 20:15:50 +01:00
|
|
|
var url = getURL(window);
|
|
|
|
if (!url){
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
var error = new Error();
|
|
|
|
apis.notify({
|
|
|
|
url,
|
|
|
|
errorStack: error.stack,
|
|
|
|
messageId: "preBlock",
|
|
|
|
timestamp: new Date(),
|
|
|
|
functionName: name,
|
|
|
|
dataURL: false
|
|
|
|
});
|
|
|
|
return;
|
2017-12-11 20:59:56 +01:00
|
|
|
}
|
2017-12-04 00:26:26 +01:00
|
|
|
}, window),
|
|
|
|
set: exportFunction(function(){}, window)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
preIntercepted = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const undoPreIntercept = function(){
|
|
|
|
if (preIntercepted){
|
|
|
|
preIntercepted = false;
|
2018-08-27 00:23:19 +02:00
|
|
|
forEachFunction(function({name, object}){
|
2018-09-05 15:50:59 +02:00
|
|
|
const originalPropertyDescriptor = originalPropertyDescriptors[name].get(object);
|
|
|
|
if (originalPropertyDescriptor){
|
|
|
|
Object.defineProperty(
|
|
|
|
object,
|
|
|
|
name,
|
|
|
|
originalPropertyDescriptor
|
|
|
|
);
|
|
|
|
}
|
2017-12-04 00:26:26 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const doRealIntercept = function(){
|
|
|
|
if (!intercepted){
|
|
|
|
scope.intercept({subject: window}, apis);
|
|
|
|
intercepted = true;
|
|
|
|
}
|
|
|
|
};
|
2017-12-18 23:55:13 +01:00
|
|
|
|
2017-12-04 00:26:26 +01:00
|
|
|
doPreIntercept();
|
|
|
|
settings.onloaded(function(){
|
|
|
|
undoPreIntercept();
|
|
|
|
doRealIntercept();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
2018-07-13 16:58:13 +02:00
|
|
|
|
2018-01-03 10:59:56 +01:00
|
|
|
let extensionID = browser.extension.getURL("");
|
2017-06-25 22:22:17 +02:00
|
|
|
scope.intercept = function intercept({subject: window}, {check, checkStack, ask, notify, prefs}){
|
2018-07-13 16:58:13 +02:00
|
|
|
function getDataURL(object, prefs){
|
|
|
|
return (
|
2018-09-11 23:56:44 +02:00
|
|
|
object &&
|
2018-07-13 16:58:13 +02:00
|
|
|
prefs("storeImageForInspection") &&
|
|
|
|
prefs("showNotifications")?
|
|
|
|
(
|
2018-09-11 23:56:44 +02:00
|
|
|
object instanceof HTMLCanvasElement?
|
|
|
|
object.toDataURL():
|
2018-07-13 16:58:13 +02:00
|
|
|
(
|
2018-09-11 23:56:44 +02:00
|
|
|
object.canvas instanceof HTMLCanvasElement?
|
|
|
|
object.canvas.toDataURL():
|
2018-07-13 16:58:13 +02:00
|
|
|
false
|
|
|
|
)
|
|
|
|
):
|
|
|
|
false
|
|
|
|
);
|
|
|
|
}
|
|
|
|
function generateChecker(name, changedFunction, siteStatus, original){
|
|
|
|
return function checker(callingDepth = 2){
|
|
|
|
var url = getURL(window);
|
|
|
|
if (!url){
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
var error = new Error();
|
|
|
|
try {
|
|
|
|
// return original if the extension itself requested the function
|
|
|
|
if (
|
|
|
|
error.stack
|
|
|
|
.split("\n", callingDepth + 2)[callingDepth + 1]
|
|
|
|
.split("@", callingDepth + 1)[1]
|
|
|
|
.startsWith(extensionID)
|
|
|
|
){
|
|
|
|
return {allow: true, original, window};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
// stack had an unknown form
|
|
|
|
}
|
|
|
|
if (checkStack(error.stack)){
|
|
|
|
return {allow: true, original, window};
|
|
|
|
}
|
2018-09-06 17:24:05 +02:00
|
|
|
var funcStatus = changedFunction.getStatus(this, siteStatus, prefs);
|
2018-07-13 16:58:13 +02:00
|
|
|
|
|
|
|
function notifyCallback(messageId){
|
|
|
|
notify({
|
|
|
|
url,
|
|
|
|
errorStack: error.stack,
|
|
|
|
messageId,
|
|
|
|
timestamp: new Date(),
|
|
|
|
functionName: name,
|
|
|
|
api: changedFunction.api,
|
|
|
|
dataURL: getDataURL(this, prefs)
|
|
|
|
});
|
|
|
|
}
|
2018-09-08 00:05:56 +02:00
|
|
|
const protectedAPIFeatures = prefs("protectedAPIFeatures");
|
|
|
|
if (
|
|
|
|
funcStatus.active &&
|
|
|
|
(
|
|
|
|
!protectedAPIFeatures.hasOwnProperty(name) ||
|
|
|
|
protectedAPIFeatures[name]
|
|
|
|
)
|
|
|
|
){
|
2018-07-13 16:58:13 +02:00
|
|
|
if (funcStatus.mode === "ask"){
|
|
|
|
funcStatus.mode = ask({
|
|
|
|
window: window,
|
|
|
|
type: changedFunction.type,
|
|
|
|
api: changedFunction.api,
|
|
|
|
canvas: this instanceof HTMLCanvasElement?
|
|
|
|
this:
|
|
|
|
(
|
|
|
|
this &&
|
|
|
|
(this.canvas instanceof HTMLCanvasElement)?
|
|
|
|
this.canvas:
|
|
|
|
false
|
|
|
|
),
|
|
|
|
errorStack: error.stack
|
|
|
|
});
|
|
|
|
}
|
|
|
|
switch (funcStatus.mode){
|
|
|
|
case "allow":
|
|
|
|
return {allow: true, original, window};
|
|
|
|
case "fake":
|
|
|
|
setRandomSupplyByType(prefs("rng"));
|
|
|
|
return {
|
|
|
|
allow: "fake",
|
|
|
|
prefs,
|
|
|
|
notify: notifyCallback,
|
|
|
|
window,
|
|
|
|
original
|
|
|
|
};
|
|
|
|
//case "block":
|
|
|
|
default:
|
|
|
|
return {
|
|
|
|
allow: false,
|
|
|
|
notify: notifyCallback
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return {allow: true, original, window};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-01-31 20:00:02 +01:00
|
|
|
var siteStatus = check({url: getURL(window)});
|
2017-11-07 00:36:44 +01:00
|
|
|
logging.verbose("status for page", window, siteStatus);
|
2016-11-19 15:35:00 +01:00
|
|
|
if (siteStatus.mode !== "allow"){
|
|
|
|
apiNames.forEach(function(name){
|
|
|
|
var changedFunction = changedFunctions[name];
|
2018-09-06 17:24:05 +02:00
|
|
|
var functionStatus = changedFunction.getStatus(undefined, siteStatus, prefs);
|
2017-11-09 00:53:52 +01:00
|
|
|
logging.verbose("status for", name, ":", functionStatus);
|
|
|
|
if (functionStatus.active){
|
2017-10-05 19:00:00 +02:00
|
|
|
(
|
|
|
|
Array.isArray(changedFunction.object)?
|
|
|
|
changedFunction.object:
|
|
|
|
[changedFunction.object]
|
|
|
|
).forEach(function(object){
|
2017-10-07 23:26:04 +02:00
|
|
|
var constructor = getWrapped(window)[object];
|
2017-02-22 17:46:17 +01:00
|
|
|
if (constructor){
|
|
|
|
var original = constructor.prototype[name];
|
2018-07-13 16:58:13 +02:00
|
|
|
const checker = generateChecker(name, changedFunction, siteStatus, original);
|
|
|
|
var descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, name);
|
2018-09-05 15:50:59 +02:00
|
|
|
if (descriptor){
|
|
|
|
if (descriptor.hasOwnProperty("value")){
|
|
|
|
if (changedFunction.fakeGenerator){
|
|
|
|
descriptor.value = exportFunction(
|
|
|
|
changedFunction.fakeGenerator(checker),
|
|
|
|
window
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
descriptor.value = null;
|
|
|
|
}
|
2016-11-13 15:09:03 +01:00
|
|
|
}
|
2018-07-13 16:58:13 +02:00
|
|
|
else {
|
2018-09-05 15:50:59 +02:00
|
|
|
descriptor.get = exportFunction(function(){
|
|
|
|
return exportFunction(
|
|
|
|
changedFunction.fakeGenerator(checker),
|
|
|
|
window
|
|
|
|
);
|
|
|
|
}, window);
|
2018-07-13 16:58:13 +02:00
|
|
|
}
|
2018-09-05 15:50:59 +02:00
|
|
|
Object.defineProperty(constructor.prototype, name, descriptor);
|
2018-07-13 16:58:13 +02:00
|
|
|
}
|
2017-02-22 17:46:17 +01:00
|
|
|
}
|
2016-12-26 14:36:01 +01:00
|
|
|
});
|
2016-11-19 15:35:00 +01:00
|
|
|
}
|
|
|
|
});
|
2018-08-27 00:23:19 +02:00
|
|
|
changedGetters.forEach(function(changedGetter){
|
|
|
|
const name = changedGetter.name;
|
2018-09-06 17:24:05 +02:00
|
|
|
var functionStatus = changedGetter.getStatus(undefined, siteStatus, prefs);
|
2018-08-27 00:23:19 +02:00
|
|
|
logging.verbose("status for", changedGetter, ":", functionStatus);
|
|
|
|
if (functionStatus.active){
|
|
|
|
changedGetter.objectGetters.forEach(function(objectGetter){
|
|
|
|
const object = objectGetter(getWrapped(window));
|
|
|
|
if (object){
|
|
|
|
const descriptor = Object.getOwnPropertyDescriptor(object, name);
|
2018-09-05 15:50:59 +02:00
|
|
|
if (descriptor && descriptor.hasOwnProperty("get")){
|
2018-08-27 00:23:19 +02:00
|
|
|
var original = descriptor.get;
|
|
|
|
const checker = generateChecker(name, changedGetter, siteStatus, original);
|
|
|
|
const getter = changedGetter.getterGenerator(checker);
|
|
|
|
descriptor.get = exportFunction(getter, window);
|
|
|
|
|
2018-09-04 23:29:58 +02:00
|
|
|
if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){
|
2018-09-05 15:53:01 +02:00
|
|
|
const setter = changedGetter.setterGenerator(window, descriptor.set, prefs);
|
2018-08-27 00:23:19 +02:00
|
|
|
descriptor.set = exportFunction(setter, window);
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.defineProperty(object, name, descriptor);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
logging.error("Try to fake non getter property:", changedGetter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2016-11-19 15:35:00 +01:00
|
|
|
}
|
2016-02-13 12:48:22 +01:00
|
|
|
};
|
2016-02-13 12:28:36 +01:00
|
|
|
}());
|