From 14a4d1cdc2d92010cc9a882a15582d9e9cfe9a8d Mon Sep 17 00:00:00 2001 From: kkapsner Date: Thu, 12 Dec 2019 00:09:53 +0100 Subject: [PATCH] Get rid of eval. --- lib/extension.js | 26 ++++++++++++++++++++++++++ lib/frame.js | 3 +-- lib/iframeProtection.js | 29 ++++++++++++++++------------- lib/intercept.js | 18 +++++++++--------- lib/modifiedAPIFunctions.js | 4 ---- lib/modifiedCanvasAPI.js | 9 +++++---- lib/modifiedDOMRectAPI.js | 27 ++++++++++++++------------- lib/modifiedNavigatorAPI.js | 8 ++++---- test/detectionTest.js | 11 +++++++++++ test/iframeTest.html | 11 ++++++++++- 10 files changed, 96 insertions(+), 50 deletions(-) diff --git a/lib/extension.js b/lib/extension.js index ea11cbb..d9570e8 100644 --- a/lib/extension.js +++ b/lib/extension.js @@ -44,5 +44,31 @@ }; Object.seal(scope.message); + scope.getWrapped = function getWrapped(obj){ + return obj && (obj.wrappedJSObject || obj); + }; + + scope.exportFunctionWithName = function exportFunctionWithName(func, context, name){ + const exportedTry = exportFunction(func, context); + if (exportedTry.name === name){ + return exportedTry; + } + else { + const wrappedContext = scope.getWrapped(context); + const options = { + defineAs: name + }; + const oldDescriptor = Object.getOwnPropertyDescriptor(wrappedContext, name); + const exported = exportFunction(func, context, options); + if (oldDescriptor){ + Object.defineProperty(wrappedContext, name, oldDescriptor); + } + else { + delete wrappedContext[name]; + } + return exported; + } + }; + Object.seal(scope); }()); \ No newline at end of file diff --git a/lib/frame.js b/lib/frame.js index 6e5a376..5f610d9 100644 --- a/lib/frame.js +++ b/lib/frame.js @@ -9,7 +9,6 @@ const {ask} = require("./askForPermission"); const {sha256String: hashing} = require("./hash"); const {check: originalCheck, checkStack: originalCheckStack} = require("./check"); - const {getWrapped} = require("./modifiedAPIFunctions"); const extension = require("./extension"); const iframeProtection = require("./iframeProtection"); @@ -133,7 +132,7 @@ try { // eslint-disable-next-line no-unused-vars const href = window.location.href; - wrappedTry = getWrapped(window); + wrappedTry = extension.getWrapped(window); } catch (error){ // we are unable to read the location due to SOP diff --git a/lib/iframeProtection.js b/lib/iframeProtection.js index 9e1295f..d4187e6 100644 --- a/lib/iframeProtection.js +++ b/lib/iframeProtection.js @@ -13,6 +13,7 @@ } const settings = require("./settings"); + const extension = require("./extension"); const lists = require("./lists"); function isWhitelisted(url){ @@ -129,40 +130,42 @@ protectionDefinition.methods.forEach(function(method){ const descriptor = Object.getOwnPropertyDescriptor(object, method); const original = descriptor.value; - changeProperty(object, method, "value", exportFunction(eval(`(function ${method}(){ + changeProperty(object, method, "value", extension.exportFunctionWithName(function method(){ const value = arguments.length? original.apply(this, window.Array.from(arguments)): original.call(this); allCallback(); return value; - })`), window)); + }, window, method)); }); protectionDefinition.getters.forEach(function(property){ - const temp = eval(`({ - get ${property}(){ - const ret = this.${property}; + const descriptor = Object.getOwnPropertyDescriptor(object, property); + const temp = { + get [property](){ + const ret = this[property]; allCallback(); return ret; } - })`); - changeProperty(object, property, "get", exportFunction( + }; + changeProperty(object, property, "get", extension.exportFunctionWithName( Object.getOwnPropertyDescriptor(temp, property).get, - window + window, + descriptor.get )); }); protectionDefinition.setters.forEach(function(property){ const descriptor = Object.getOwnPropertyDescriptor(object, property); const setter = descriptor.set; - const temp = eval(`({ - set ${property}(value){ + const temp = { + set [property](value){ const ret = setter.call(this, value); // const ret = this.${property} = value; allCallback(); return ret; } - })`); - changeProperty(object, property, "set", exportFunction( - Object.getOwnPropertyDescriptor(temp, property).set, window + }; + changeProperty(object, property, "set", extension.exportFunctionWithName( + Object.getOwnPropertyDescriptor(temp, property).set, window, setter.name )); }); }); diff --git a/lib/intercept.js b/lib/intercept.js index 58c7588..7559b48 100644 --- a/lib/intercept.js +++ b/lib/intercept.js @@ -14,7 +14,6 @@ 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"); @@ -68,7 +67,7 @@ [changedFunction.object] ).map(function(name){ if (name){ - const constructor = getWrapped(windowToProcess)[name]; + const constructor = extension.getWrapped(windowToProcess)[name]; if (constructor){ return constructor.prototype; } @@ -77,7 +76,7 @@ }).concat( changedFunction.objectGetters? changedFunction.objectGetters.map(function(objectGetter){ - return objectGetter(getWrapped(windowToProcess)); + return objectGetter(extension.getWrapped(windowToProcess)); }): [] ); @@ -96,7 +95,7 @@ changedGetters.forEach(function(changedGetter){ const name = changedGetter.name; changedGetter.objectGetters.forEach(function(changedGetter){ - const object = changedGetter(getWrapped(windowToProcess)); + const object = changedGetter(extension.getWrapped(windowToProcess)); if (object){ callback({name, object, objectGetter: changedGetter}); } @@ -359,7 +358,7 @@ logging.verbose("status for", changedGetter, ":", functionStatus); if (functionStatus.active){ changedGetter.objectGetters.forEach(function(objectGetter){ - const object = objectGetter(getWrapped(windowToProcess)); + const object = objectGetter(extension.getWrapped(windowToProcess)); if (object){ const descriptor = Object.getOwnPropertyDescriptor(object, name); if (descriptor && descriptor.hasOwnProperty("get")){ @@ -369,15 +368,16 @@ window: windowToProcess, prefs, checkStack, ask, notify }); const getter = changedGetter.getterGenerator(checker, original, windowToProcess); - descriptor.get = exportFunction(getter, windowToProcess); + descriptor.get = extension.exportFunctionWithName(getter, windowToProcess, original.name); - if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){ + if (descriptor.hasOwnProperty("set") && descriptor.set && changedGetter.setterGenerator){ + const original = descriptor.set; const setter = changedGetter.setterGenerator( windowToProcess, - descriptor.set, + original, prefs ); - descriptor.set = exportFunction(setter, windowToProcess); + descriptor.set = extension.exportFunctionWithName(setter, windowToProcess, original.name); } Object.defineProperty(object, name, descriptor); diff --git a/lib/modifiedAPIFunctions.js b/lib/modifiedAPIFunctions.js index ab6d565..7940c75 100644 --- a/lib/modifiedAPIFunctions.js +++ b/lib/modifiedAPIFunctions.js @@ -12,10 +12,6 @@ scope = require.register("./modifiedAPIFunctions", {}); } - scope.getWrapped = function getWrapped(obj){ - return obj && (obj.wrappedJSObject || obj); - }; - scope.checkerWrapper = function checkerWrapper(checker, object, args, callback){ const check = checker.call(object); if (check.allow){ diff --git a/lib/modifiedCanvasAPI.js b/lib/modifiedCanvasAPI.js index f18c709..04f43b1 100644 --- a/lib/modifiedCanvasAPI.js +++ b/lib/modifiedCanvasAPI.js @@ -7,8 +7,9 @@ const scope = ((typeof exports) !== "undefined")? exports: require.register("./modifiedCanvasAPI"); const colorStatistics = require("./colorStatistics"); const logging = require("./logging"); + const extension = require("./extension"); const {copyCanvasToWebgl} = require("./webgl"); - const {getWrapped, checkerWrapper} = require("./modifiedAPIFunctions"); + const {checkerWrapper} = require("./modifiedAPIFunctions"); let randomSupply = null; @@ -23,8 +24,8 @@ let imageData; let source; if ((context.canvas.width || 0) * (context.canvas.height || 0) === 0){ - imageData = new (getWrapped(window).ImageData)(0, 0); - source = new (getWrapped(window).ImageData)(0, 0); + imageData = new (extension.getWrapped(window).ImageData)(0, 0); + source = new (extension.getWrapped(window).ImageData)(0, 0); } else if (context instanceof window.CanvasRenderingContext2D){ imageData = window.CanvasRenderingContext2D.prototype.getImageData.call( @@ -35,7 +36,7 @@ source = imageData.data; } else { - imageData = new (getWrapped(window).ImageData)(context.canvas.width, context.canvas.height); + imageData = new (extension.getWrapped(window).ImageData)(context.canvas.width, context.canvas.height); source = new Uint8Array(imageData.data.length); ( context instanceof window.WebGLRenderingContext? diff --git a/lib/modifiedDOMRectAPI.js b/lib/modifiedDOMRectAPI.js index 983d23f..4e929a6 100644 --- a/lib/modifiedDOMRectAPI.js +++ b/lib/modifiedDOMRectAPI.js @@ -12,7 +12,8 @@ scope = require.register("./modifiedDOMRectAPI", {}); } - const {getWrapped, checkerWrapper, setProperties, getStatusByFlag} = require("./modifiedAPIFunctions"); + const extension = require("./extension"); + const {checkerWrapper, setProperties, getStatusByFlag} = require("./modifiedAPIFunctions"); const {byteArrayToString: hash} = require("./hash"); @@ -30,7 +31,7 @@ const registeredRects = new WeakMap(); function registerDOMRect(domRect, notify, window, prefs){ - registeredRects.set(getWrapped(domRect), { + registeredRects.set(extension.getWrapped(domRect), { notify: function(){ let done = false; return function(message){ @@ -45,7 +46,7 @@ }); } function getDOMRectRegistration(domRect){ - return registeredRects.get(getWrapped(domRect)); + return registeredRects.get(extension.getWrapped(domRect)); } const cache = {}; @@ -159,8 +160,8 @@ ], name: property, getterGenerator: function(){ - const temp = eval(`({ - get ${property}(){ + const temp = { + get [property](){ const registration = getDOMRectRegistration(this); if (registration){ return getFakeDomRect( @@ -168,24 +169,24 @@ this, registration.prefs, registration.notify - ).${property}; + )[property]; } - return this.${property}; + return this[property]; } - })`); + }; return Object.getOwnPropertyDescriptor(temp, property).get; } }; if (!readonly){ changedGetter.setterGenerator = function(window, original, prefs){ - const temp = eval(`({ - set ${property}(newValue){ + const temp = { + set [property](newValue){ const registration = getDOMRectRegistration(this); if (registration){ const fakeDomRect = getFakeDomRect(window, this, prefs, registration.notify); - registeredRects.delete(getWrapped(this)); + registeredRects.delete(extension.getWrapped(this)); ["x", "y", "width", "height"].forEach((prop) => { - if (prop === "${property}"){ + if (prop === property){ this[prop] = newValue; } else { @@ -197,7 +198,7 @@ original.apply(this, window.Array.from(arguments)); } } - })`); + }; return Object.getOwnPropertyDescriptor(temp, property).set; }; } diff --git a/lib/modifiedNavigatorAPI.js b/lib/modifiedNavigatorAPI.js index 6cca5a4..f35e5cc 100644 --- a/lib/modifiedNavigatorAPI.js +++ b/lib/modifiedNavigatorAPI.js @@ -20,19 +20,19 @@ objectGetters: [function(window){return window.Navigator && window.Navigator.prototype;}], name: property, getterGenerator: function(checker){ - const temp = eval(`({ - get ${property}(){ + const temp = { + get [property](){ return checkerWrapper(checker, this, arguments, function(args, check){ const {notify, window, original} = check; const originalValue = original.apply(this, window.Array.from(args)); - const returnValue = navigator.getNavigatorValue("${property}"); + const returnValue = navigator.getNavigatorValue(property); if (originalValue !== returnValue){ notify("fakedNavigatorReadout"); } return returnValue; }); } - })`); + }; return Object.getOwnPropertyDescriptor(temp, property).get; } }; diff --git a/test/detectionTest.js b/test/detectionTest.js index eef4338..e84c6d5 100644 --- a/test/detectionTest.js +++ b/test/detectionTest.js @@ -205,6 +205,17 @@ addTest("function name", function(log){ }, ].map(checkName).some(function(b){return b;}); }); +addTest("exposed getters or setters", function(log){ + "use strict"; + + return Object.keys(window).filter(function(key){ + if (/^(get|set) /.test(key)){ + log("found exposed function", JSON.stringify(key)); + return true; + } + return false; + }).length !== 0; +}); addTest("property descriptor", function(log){ "use strict"; diff --git a/test/iframeTest.html b/test/iframeTest.html index f58828e..059f87d 100644 --- a/test/iframeTest.html +++ b/test/iframeTest.html @@ -88,7 +88,16 @@ "use strict"; document.body.innerHTML = ""; - console.log("TEST:", "innerHTML after 1000ms:", compare(test(window[0]), reference)); + log("TEST:", "innerHTML after 1000ms:", compare(test(window[0]), reference)); + + var iFrame = document.createElement("iframe"); + document.body.appendChild(iFrame); + log("TEST:", "appendChild after 1000ms:", compare(test(window[1]), reference)); + + var iFrame2 = document.createElement("iframe"); + iFrame.replaceWith(iFrame2); + log("TEST:", "replaceWith after 1000ms:", compare(test(window[1]), reference)); + document.body.innerHTML = "

Iframe protection

" + "Open console (Ctrl + Shift + K) to see results. " + "Depending on your Browser version you might have to check the \"Persist Logs\" flag and reload the page.
" +