From adabad6ac6ece1b41525a93df0d0e8499542e973 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Mon, 22 Jul 2019 18:27:26 +0800 Subject: [PATCH 1/2] fix: pollution to outside world Function.prototype.constructor --- src/repair/functions.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/repair/functions.js b/src/repair/functions.js index 9044a03..6dc3792 100644 --- a/src/repair/functions.js +++ b/src/repair/functions.js @@ -16,7 +16,6 @@ * a copy has been made, and funtions can only be created by syntax (using eval) * or by invoking a previously saved reference to the originals. */ - // todo: this file should be moved out to a separate repo and npm module. const globalEval = eval; export function repairFunctions() { @@ -46,11 +45,21 @@ export function repairFunctions() { throw e; } const FunctionPrototype = getPrototypeOf(FunctionInstance); + const oldFunctionConstructor = FunctionPrototype.constructor; + function isRunningInRealms() { + const e = new Error().stack; + if (!e) return true; + return e.indexOf('eval') !== -1; + } // Prevents the evaluation of source when calling constructor on the // prototype of functions. const TamedFunction = function() { - throw new TypeError('Not available'); + if (isRunningInRealms()) { + throw new TypeError('Not available'); + } else { + return oldFunctionConstructor.apply(this, arguments); + } }; defineProperties(TamedFunction, { name: { value: name } }); From 7efe73c4b6c5b704f2605b913373fd68b855871a Mon Sep 17 00:00:00 2001 From: Jack Works Date: Sat, 28 Sep 2019 11:11:13 +0800 Subject: [PATCH 2/2] test: add test for new behavior of inner Function constructor --- test/module/functions.js | 101 +++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 10 deletions(-) diff --git a/test/module/functions.js b/test/module/functions.js index baba918..7f80e9e 100644 --- a/test/module/functions.js +++ b/test/module/functions.js @@ -1,29 +1,70 @@ import test from 'tape'; import { repairFunctions } from '../../src/repair/functions'; +import Realm from '../../src/realm'; test('repairFunctions', specs => { repairFunctions(); specs.test('Function.prototype.constructor', t => { - t.plan(4); + t.plan(7); // eslint-disable-next-line no-new-func t.doesNotThrow(() => Function('')); // eslint-disable-next-line no-proto - t.throws(() => Error.__proto__.constructor(''), TypeError); - t.throws(() => Function.prototype.constructor(''), TypeError); + t.doesNotThrow(() => Error.__proto__.constructor('')); + t.doesNotThrow(() => Function.prototype.constructor('')); const proto = Object.getPrototypeOf((0, eval)('(function() {})')); - t.throws(() => proto.constructor(''), TypeError); + t.doesNotThrow(() => proto.constructor('')); + + const realms = Realm.makeCompartment(); + + // eslint-disable-next-line no-proto + t.throws( + () => realms.evaluate("Error.__proto__.constructor('')"), + TypeError + ); + t.throws( + () => realms.evaluate("Function.prototype.constructor('')"), + TypeError + ); + + t.throws( + () => + realms.evaluate(` + const proto = Object.getPrototypeOf((0, eval)('(function() {})')); + proto.constructor('')`), + TypeError + ); }); specs.test('AsyncFunction.constructor', t => { - t.plan(1); + t.plan(2); try { const proto = Object.getPrototypeOf((0, eval)('(async function() {})')); - t.throws(() => proto.constructor(''), TypeError); + t.doesNotThrow(() => proto.constructor('')); + } catch (e) { + if ( + e instanceof SyntaxError && + e.message.startsWith('Unexpected token') + ) { + t.pass('not supported'); + } else { + throw e; + } + } + + try { + const realms = Realm.makeCompartment(); + t.throws( + () => + realms.evaluate(` + const proto = Object.getPrototypeOf((0, eval)('(async function() {})')); + proto.constructor('')`), + TypeError + ); } catch (e) { if ( e instanceof SyntaxError && @@ -37,11 +78,31 @@ test('repairFunctions', specs => { }); specs.test('GeneratorFunction.constructor', t => { - t.plan(1); + t.plan(2); try { const proto = Object.getPrototypeOf((0, eval)('(function* () {})')); - t.throws(() => proto.constructor(''), TypeError); + t.doesNotThrow(() => proto.constructor('')); + } catch (e) { + if ( + e instanceof SyntaxError && + e.message.startsWith('Unexpected token') + ) { + t.pass('not supported'); + } else { + throw e; + } + } + try { + const realm = Realm.makeCompartment(); + t.throws(() => + realm.evaluate( + ` + const proto = Object.getPrototypeOf((0, eval)('(function* () {})')); + proto.constructor('');`, + TypeError + ) + ); } catch (e) { if ( e instanceof SyntaxError && @@ -55,11 +116,31 @@ test('repairFunctions', specs => { }); specs.test('AsyncGeneratorFunction.constructor', t => { - t.plan(1); + t.plan(2); try { const proto = Object.getPrototypeOf((0, eval)('(async function* () {})')); - t.throws(() => proto.constructor(''), TypeError); + t.doesNotThrow(() => proto.constructor('')); + } catch (e) { + if ( + e instanceof SyntaxError && + e.message.startsWith('Unexpected token') + ) { + t.pass('not supported'); + } else { + throw e; + } + } + try { + const realm = Realm.makeCompartment(); + t.throws( + () => + realm.evaluate(` + const proto = Object.getPrototypeOf((0, eval)('(async function* () {})')); + proto.constructor(''); + `), + TypeError + ); } catch (e) { if ( e instanceof SyntaxError &&