<commit_before>/* 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";
	
	let scope;
	if ((typeof exports) !== "undefined"){
		scope = exports;
	}
	else {
		scope = require.register("./intercept", {});
	}
	
	const {changedFunctions, changedGetters, setRandomSupply} = require("./modifiedAPI");
	const randomSupplies = require("./randomSupplies");
	const {getWrapped} = require("./modifiedAPIFunctions");
	const logging = require("./logging");
	const settings = require("./settings");
	const extension = require("./extension");

	setRandomSupply(randomSupplies.nonPersistent);
	const apiNames = Object.keys(changedFunctions);
	let undef;
	function setRandomSupplyByType(type){
		switch (type){
			case "persistent":
				setRandomSupply(randomSupplies.persistent);
				break;
			case "constant":
				setRandomSupply(randomSupplies.constant);
				break;
			case "white":
				setRandomSupply(randomSupplies.white);
				break;
			default:
				setRandomSupply(randomSupplies.nonPersistent);
		}
	}
	settings.on("rng", function(){
		setRandomSupplyByType(settings.rng);
	});
	
	function getURL(window){
		let 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"){
			if (window !== window.parent){
				return getURL(window.parent);
			}
			else if (window.opener){
				return getURL(window.opener);
			}
		}
		return href;
	}
	
	scope.preIntercept = function preIntercept({subject: window}, apis){
		if (!settings.isStillDefault){
			logging.message("settings already loaded -> no need to pre intercept");
			scope.intercept({subject: window}, apis);
		}
		else  {
			logging.message("settings not loaded -> need to pre intercept");
			let forceLoad = true;
			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){
						const constructor = getWrapped(window)[object];
						if (constructor){
							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});
						}
					});
				});
			};
			let originalPropertyDescriptors = {};
			const doPreIntercept = function(){
				if (!preIntercepted){
					forEachFunction(function({name, object}){
						const map = originalPropertyDescriptors[name] || new WeakMap();
						originalPropertyDescriptors[name] = map;
						
						const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(object, name);
						if (!originalPropertyDescriptor){
							return;
						}
						
						map.set(object, originalPropertyDescriptor);
						Object.defineProperty(
							object,
							name,
							{
								enumerable: true,
								configurable: true,
								get: exportFunction(function(){
									if (forceLoad){
										logging.warning("force load the settings. Calling stack:", (new Error()).stack);
										undoPreIntercept();
										settings.forceLoad();
										doRealIntercept();
										const descriptor = Object.getOwnPropertyDescriptor(object, name);
										return descriptor.value || descriptor.get.call(this);
									}
									else {
										logging.notice("API blocked (%s)", name);
										const url = getURL(window);
										if (!url){
											return undef;
										}
										const error = new Error();
										apis.notify({
											url,
											errorStack: error.stack,
											messageId: "preBlock",
											timestamp: new Date(),
											functionName: name,
											dataURL: false
										});
										return;
									}
								}, window),
								set: exportFunction(function(){}, window)
							}
						);
					});
					preIntercepted = true;
				}
			};
			const undoPreIntercept = function(){
				if (preIntercepted){
					preIntercepted = false;
					forEachFunction(function({name, object}){
						const originalPropertyDescriptor = originalPropertyDescriptors[name].get(object);
						if (originalPropertyDescriptor){
							Object.defineProperty(
								object,
								name,
								originalPropertyDescriptor
							);
						}
					});
				}
			};
			const doRealIntercept = function(){
				if (!intercepted){
					scope.intercept({subject: window}, apis);
					intercepted = true;
				}
			};
			
			doPreIntercept();
			settings.onloaded(function(){
				undoPreIntercept();
				doRealIntercept();
			});
		}
	};
	
	let extensionID = extension.extensionID;
	scope.intercept = function intercept({subject: window}, {check, checkStack, ask, notify, prefs}){
		function getDataURL(object, prefs){
			return (
				object &&
				prefs("storeImageForInspection") &&
				prefs("showNotifications")?
					(
						object instanceof HTMLCanvasElement?
							object.toDataURL():
							(
								object.canvas instanceof HTMLCanvasElement?
									object.canvas.toDataURL():
									false
							)
					):
					false
			);
		}
		function generateChecker(name, changedFunction, siteStatus, original){
			return function checker(callingDepth = 2){
				const error = new Error();
				const errorStack = error.stack;
				
				try {
					// return original if the extension itself requested the function
					if (
						errorStack
							.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(errorStack)){
					return {allow: true, original, window};
				}
				const funcStatus = changedFunction.getStatus(this, siteStatus, prefs);
				
				const This = this;
				function notifyCallback(messageId){
					notify({
						url: getURL(window),
						errorStack,
						messageId,
						timestamp: new Date(),
						functionName: name,
						api: changedFunction.api,
						dataURL: getDataURL(This, prefs)
					});
				}
				const protectedAPIFeatures = prefs("protectedAPIFeatures");
				if (
					funcStatus.active &&
					(
						!protectedAPIFeatures.hasOwnProperty(name) ||
						protectedAPIFeatures[name]
					)
				){
					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
						});
					}
					switch (funcStatus.mode){
						case "allow":
							return {allow: true, original, window};
						case "fake":
							return {
								allow: "fake",
								prefs,
								notify: notifyCallback,
								window,
								original
							};
						//case "block":
						default:
							return {
								allow: false,
								notify: notifyCallback
							};
					}
				}
				else {
					return {allow: true, original, window};
				}
			};
		}
		
		const siteStatus = check({url: getURL(window)});
		logging.verbose("status for page", window, siteStatus);
		if (siteStatus.mode !== "allow"){
			apiNames.forEach(function(name){
				const changedFunction = changedFunctions[name];
				const functionStatus = changedFunction.getStatus(undefined, siteStatus, prefs);
				logging.verbose("status for", name, ":", functionStatus);
				if (functionStatus.active){
					(
						Array.isArray(changedFunction.object)?
							changedFunction.object:
							[changedFunction.object]
					).forEach(function(object){
						const constructor = getWrapped(window)[object];
						if (constructor){
							const original = constructor.prototype[name];
							const checker = generateChecker(name, changedFunction, siteStatus, original);
							const descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, name);
							if (descriptor){
								if (descriptor.hasOwnProperty("value")){
									if (changedFunction.fakeGenerator){
										descriptor.value = exportFunction(
											changedFunction.fakeGenerator(checker, original, window),
											window
										);
									}
									else {
										descriptor.value = null;
									}
								}
								else {
									descriptor.get = exportFunction(function(){
										return exportFunction(
											changedFunction.fakeGenerator(checker),
											window
										);
									}, window);
								}
								Object.defineProperty(constructor.prototype, name, descriptor);
							}
						}
					});
				}
			});
			changedGetters.forEach(function(changedGetter){
				const name = changedGetter.name;
				const functionStatus = changedGetter.getStatus(undefined, siteStatus, prefs);
				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);
							if (descriptor && descriptor.hasOwnProperty("get")){
								const original = descriptor.get;
								const checker = generateChecker(name, changedGetter, siteStatus, original);
								const getter = changedGetter.getterGenerator(checker, original, window);
								descriptor.get = exportFunction(getter, window);
								
								if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){
									const setter = changedGetter.setterGenerator(window, descriptor.set, prefs);
									descriptor.set = exportFunction(setter, window);
								}
								
								Object.defineProperty(object, name, descriptor);
							}
							else if (
								changedGetter.valueGenerator &&<commit_msg>Added ability to use objectGetters on faked functions.<commit_after>/* 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";
	
	let scope;
	if ((typeof exports) !== "undefined"){
		scope = exports;
	}
	else {
		scope = require.register("./intercept", {});
	}
	
	const {changedFunctions, changedGetters, setRandomSupply} = require("./modifiedAPI");
	const randomSupplies = require("./randomSupplies");
	const {getWrapped} = require("./modifiedAPIFunctions");
	const logging = require("./logging");
	const settings = require("./settings");
	const extension = require("./extension");

	setRandomSupply(randomSupplies.nonPersistent);
	const apiNames = Object.keys(changedFunctions);
	let undef;
	function setRandomSupplyByType(type){
		switch (type){
			case "persistent":
				setRandomSupply(randomSupplies.persistent);
				break;
			case "constant":
				setRandomSupply(randomSupplies.constant);
				break;
			case "white":
				setRandomSupply(randomSupplies.white);
				break;
			default:
				setRandomSupply(randomSupplies.nonPersistent);
		}
	}
	settings.on("rng", function(){
		setRandomSupplyByType(settings.rng);
	});
	
	function getURL(window){
		let 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"){
			if (window !== window.parent){
				return getURL(window.parent);
			}
			else if (window.opener){
				return getURL(window.opener);
			}
		}
		return href;
	}
	
	scope.preIntercept = function preIntercept({subject: window}, apis){
		if (!settings.isStillDefault){
			logging.message("settings already loaded -> no need to pre intercept");
			scope.intercept({subject: window}, apis);
		}
		else  {
			logging.message("settings not loaded -> need to pre intercept");
			let forceLoad = true;
			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]
					).map(function(name){
						if (name){
							const constructor = getWrapped(window)[name];
							if (constructor){
								return constructor.prototype;
							}
						}
						return false;
					}).concat(
						changedFunction.objectGetters?
							changedFunction.objectGetters.map(function(objectGetter){
								return objectGetter(getWrapped(window));
							}):
							[]
					).forEach(function(object){
						if (object){
							callback({name, object: object});
						}
					});
				});
				changedGetters.forEach(function(changedGetter){
					const name = changedGetter.name;
					changedGetter.objectGetters.forEach(function(objectGetter){
						const object = objectGetter(getWrapped(window));
						if (object){
							callback({name, object});
						}
					});
				});
			};
			let originalPropertyDescriptors = {};
			const doPreIntercept = function(){
				if (!preIntercepted){
					forEachFunction(function({name, object}){
						const map = originalPropertyDescriptors[name] || new WeakMap();
						originalPropertyDescriptors[name] = map;
						
						const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(object, name);
						if (!originalPropertyDescriptor){
							return;
						}
						
						map.set(object, originalPropertyDescriptor);
						Object.defineProperty(
							object,
							name,
							{
								enumerable: true,
								configurable: true,
								get: exportFunction(function(){
									if (forceLoad){
										logging.warning("force load the settings. Calling stack:", (new Error()).stack);
										undoPreIntercept();
										settings.forceLoad();
										doRealIntercept();
										const descriptor = Object.getOwnPropertyDescriptor(object, name);
										return descriptor.value || descriptor.get.call(this);
									}
									else {
										logging.notice("API blocked (%s)", name);
										const url = getURL(window);
										if (!url){
											return undef;
										}
										const error = new Error();
										apis.notify({
											url,
											errorStack: error.stack,
											messageId: "preBlock",
											timestamp: new Date(),
											functionName: name,
											dataURL: false
										});
										return;
									}
								}, window),
								set: exportFunction(function(){}, window)
							}
						);
					});
					preIntercepted = true;
				}
			};
			const undoPreIntercept = function(){
				if (preIntercepted){
					preIntercepted = false;
					forEachFunction(function({name, object}){
						const originalPropertyDescriptor = originalPropertyDescriptors[name].get(object);
						if (originalPropertyDescriptor){
							Object.defineProperty(
								object,
								name,
								originalPropertyDescriptor
							);
						}
					});
				}
			};
			const doRealIntercept = function(){
				if (!intercepted){
					scope.intercept({subject: window}, apis);
					intercepted = true;
				}
			};
			
			doPreIntercept();
			settings.onloaded(function(){
				undoPreIntercept();
				doRealIntercept();
			});
		}
	};
	
	let extensionID = extension.extensionID;
	scope.intercept = function intercept({subject: window}, {check, checkStack, ask, notify, prefs}){
		function getDataURL(object, prefs){
			return (
				object &&
				prefs("storeImageForInspection") &&
				prefs("showNotifications")?
					(
						object instanceof HTMLCanvasElement?
							object.toDataURL():
							(
								object.canvas instanceof HTMLCanvasElement?
									object.canvas.toDataURL():
									false
							)
					):
					false
			);
		}
		function generateChecker(name, changedFunction, siteStatus, original){
			return function checker(callingDepth = 2){
				const error = new Error();
				const errorStack = error.stack;
				
				try {
					// return original if the extension itself requested the function
					if (
						errorStack
							.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(errorStack)){
					return {allow: true, original, window};
				}
				const funcStatus = changedFunction.getStatus(this, siteStatus, prefs);
				
				const This = this;
				function notifyCallback(messageId){
					notify({
						url: getURL(window),
						errorStack,
						messageId,
						timestamp: new Date(),
						functionName: name,
						api: changedFunction.api,
						dataURL: getDataURL(This, prefs)
					});
				}
				const protectedAPIFeatures = prefs("protectedAPIFeatures");
				if (
					funcStatus.active &&
					(
						!protectedAPIFeatures.hasOwnProperty(name) ||
						protectedAPIFeatures[name]
					)
				){
					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
						});
					}
					switch (funcStatus.mode){
						case "allow":
							return {allow: true, original, window};
						case "fake":
							return {
								allow: "fake",
								prefs,
								notify: notifyCallback,
								window,
								original
							};
						//case "block":
						default:
							return {
								allow: false,
								notify: notifyCallback
							};
					}
				}
				else {
					return {allow: true, original, window};
				}
			};
		}
		
		const siteStatus = check({url: getURL(window)});
		logging.verbose("status for page", window, siteStatus);
		if (siteStatus.mode !== "allow"){
			apiNames.forEach(function(name){
				const changedFunction = changedFunctions[name];
				const functionStatus = changedFunction.getStatus(undefined, siteStatus, prefs);
				logging.verbose("status for", name, ":", functionStatus);
				if (functionStatus.active){
					(
						Array.isArray(changedFunction.object)?
							changedFunction.object:
							[changedFunction.object]
					).map(function(name){
						if (name){
							const constructor = getWrapped(window)[name];
							if (constructor){
								return constructor.prototype;
							}
						}
						return false;
					}).concat(
						changedFunction.objectGetters?
							changedFunction.objectGetters.map(function(objectGetter){
								return objectGetter(getWrapped(window));
							}):
							[]
					).forEach(function(object){
						if (object){
							const original = object[name];
							const checker = generateChecker(name, changedFunction, siteStatus, original);
							const descriptor = Object.getOwnPropertyDescriptor(object, name);
							if (descriptor){
								if (descriptor.hasOwnProperty("value")){
									if (changedFunction.fakeGenerator){
										descriptor.value = exportFunction(
											changedFunction.fakeGenerator(checker, original, window),
											window
										);
									}
									else {
										descriptor.value = null;
									}
								}
								else {
									descriptor.get = exportFunction(function(){
										return exportFunction(
											changedFunction.fakeGenerator(checker),
											window
										);
									}, window);
								}
								Object.defineProperty(object, name, descriptor);
							}
						}
					});
				}
			});
			changedGetters.forEach(function(changedGetter){
				const name = changedGetter.name;
				const functionStatus = changedGetter.getStatus(undefined, siteStatus, prefs);
				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);
							if (descriptor && descriptor.hasOwnProperty("get")){
								const original = descriptor.get;
								const checker = generateChecker(name, changedGetter, siteStatus, original);
								const getter = changedGetter.getterGenerator(checker, original, window);
								descriptor.get = exportFunction(getter, window);
								
								if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){
									const setter = changedGetter.setterGenerator(window, descriptor.set, prefs);
									descriptor.set = exportFunction(setter, window);
								}
								
								Object.defineProperty(object, name, descriptor);
							}
							else if (
								changedGetter.valueGenerator &&
{'fraction_non_alphanumeric': 0.11581218631267882, 'fraction_numerical': 0.0008443172756695905, 'mean_word_length': 2.567006859628576, 'pattern_counts': {'":': 16, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1305670', 'n_tokens_mistral': 8989, 'n_tokens_neox': 5806, 'n_words': 1319}