Added tests for unsafeRec
MODIFY
shim/src/unsafeRec.js
shim/src/unsafeRec.js
@@ -9,7 +9,7 @@ import { freeze } from './commons';
// We need this to implement the shim. However, when Realms land for real,
// this feature will be provided by the underlying engine instead.
-// Detection used in RollupJS.
+// Platform detection.
const isNode = typeof exports === 'object' && typeof module !== 'undefined';
const isBrowser = typeof document === 'object';
if ((!isNode && !isBrowser) || (isNode && isBrowser)) {
@@ -24,14 +24,16 @@ const vm = isNode ? require('vm') : undefined;
const unsafeGlobalSrc = "'use strict'; this";
const unsafeGlobalEvalSrc = `(0, eval)("'use strict'; this")`;
-function createNewUnsafeGlobalForNode() {
+// This method is only exported for testing purposes.
+export function createNewUnsafeGlobalForNode() {
// Use unsafeGlobalEvalSrc to ensure we get the right 'this'.
const unsafeGlobal = vm.runInNewContext(unsafeGlobalEvalSrc);
return unsafeGlobal;
}
-function createNewUnsafeGlobalForBrowser() {
+// This method is only exported for testing purposes.
+export function createNewUnsafeGlobalForBrowser() {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
@@ -47,11 +49,7 @@ function createNewUnsafeGlobalForBrowser() {
return unsafeGlobal;
}
-// we only export this so test-repair.js can get an unrepaired
-// Object.prototype, to sense if this platform has the buggy behavior
-export const getNewUnsafeGlobal = isNode
- ? createNewUnsafeGlobalForNode
- : createNewUnsafeGlobalForBrowser;
+const getNewUnsafeGlobal = isNode ? createNewUnsafeGlobalForNode : createNewUnsafeGlobalForBrowser;
// The unsafeRec is shim-specific. It acts as the mechanism to obtain a fresh
// set of intrinsics together with their associated eval and Function
@@ -59,7 +57,7 @@ export const getNewUnsafeGlobal = isNode
// tied to a set of intrinsics, aka the "undeniables". If it were possible to
// mix-and-match them from different contexts, that would enable some
// attacks.
-function createUnsafeRec(unsafeGlobal, allShims) {
+function createUnsafeRec(unsafeGlobal, allShims = []) {
const sharedGlobalDescs = getSharedGlobalDescs(unsafeGlobal);
return freeze({
@@ -89,5 +87,5 @@ export function createCurrentUnsafeRec() {
const unsafeGlobal = (0, eval)(unsafeGlobalSrc);
repairAccessors();
repairFunctions();
- return createUnsafeRec(unsafeGlobal, []);
+ return createUnsafeRec(unsafeGlobal);
}

ADD
shim/test/module/unsafeRec.js
+import test from 'tape';
+import {
+ createNewUnsafeRec,
+ createNewUnsafeGlobalForNode,
+ createNewUnsafeGlobalForBrowser
+} from '../../src/unsafeRec';
+
+const isNode = typeof exports === 'object' && typeof module !== 'undefined';
+const isBrowser = typeof document === 'object';
+
+test('createNewUnsafeRec', t => {
+ t.plan(7);
+
+ const unsafeRec = createNewUnsafeRec();
+ const { unsafeGlobal, sharedGlobalDescs, unsafeEval, unsafeFunction, allShims } = unsafeRec;
+
+ t.ok(Object.isFrozen(unsafeRec));
+
+ // todo: more thorough test of descriptors.
+ t.deepEqual(sharedGlobalDescs.Object, {
+ value: unsafeGlobal.Object,
+ configurable: true,
+ writable: true
+ });
+ t.equal(unsafeEval, unsafeGlobal.eval);
+ t.deepEqual(unsafeFunction, unsafeGlobal.Function);
+ t.deepEqual(allShims, []);
+
+ t.ok(unsafeGlobal instanceof unsafeGlobal.Object, 'global must be an Object');
+ t.notOk(unsafeGlobal instanceof Object, 'must not be Object in this realm');
+});
+
+test('createNewUnsafeGlobalForNode on node', t => {
+ if (!isNode) {
+ t.skip('Skipping, not node');
+ t.end();
+ return;
+ }
+
+ t.plan(6);
+
+ const unsafeGlobal = createNewUnsafeGlobalForNode();
+
+ t.ok(unsafeGlobal instanceof unsafeGlobal.Object, 'global must be an Object');
+ t.notOk(unsafeGlobal instanceof Object, 'must not be Object in this realm');
+
+ t.ok(unsafeGlobal.eval instanceof unsafeGlobal.Function, 'must provide eval() function');
+ t.notOk(unsafeGlobal.eval instanceof Function, 'eval() must not be Function in this realm');
+
+ t.ok(unsafeGlobal.Function instanceof unsafeGlobal.Function, 'must provide Function() function');
+ t.notOk(
+ unsafeGlobal.Function instanceof Function,
+ 'Function() must not be Function in this realm'
+ );
+});
+
+test('createNewUnsafeGlobalForNode on a browser', t => {
+ if (!isBrowser) {
+ t.skip('Skipping, not a browser');
+ t.end();
+ return;
+ }
+
+ // todo, implement browser tests
+ t.fail('not implemented yet');
+});
+
+test('createNewUnsafeGlobalForBrowser on node', t => {
+ if (!isNode) {
+ t.skip('Skipping, not node');
+ t.end();
+ return;
+ }
+
+ t.plan(3);
+
+ // eslint-disable-next-line global-require
+ const vm = require('vm');
+ const window = vm.runInNewContext('"use strict"; (0, eval)("this")');
+ const iframe = {
+ contentWindow: window,
+ style: {}
+ };
+ const body = [];
+ global.document = {
+ createElement() {
+ return iframe;
+ },
+ body: {
+ appendChild(el) {
+ body.push(el);
+ }
+ }
+ };
+
+ let unsafeGlobal;
+ try {
+ global.window = global;
+ global.document = document;
+ unsafeGlobal = createNewUnsafeGlobalForBrowser();
+ } catch (e) {
+ throw e;
+ } finally {
+ // Ensure this cleanup always occurs.
+ delete global.window;
+ delete global.document;
+ }
+
+ t.equal(unsafeGlobal, iframe.contentWindow, 'global must be from iframe');
+ t.equal(iframe.style.display, 'none', 'iframe must be hidden');
+ t.deepEqual(body, [iframe], 'iframe must be in document body');
+});
+
+test('createNewUnsafeGlobalForBrowser on a browser', t => {
+ if (!isBrowser) {
+ t.skip('Skipping, not a browser');
+ t.end();
+ return;
+ }
+
+ // todo, implement browser tests
+ t.fail('not implemented yet');
+});

MODIFY
shim/test/realm/test-repair.js
shim/test/realm/test-repair.js
import test from 'tape';
+import vm from 'vm';
import Realm from '../../src/realm';
-import { getNewUnsafeGlobal } from '../../src/unsafeRec';
// import { walkObjects } from '../../src/scan';
export const protectedObjects = new WeakMap();
@@ -25,7 +25,7 @@ function testForBug(o) {
}
test('fix the bug in which accessor methods leak the global', t => {
- const unfixedGlobal = getNewUnsafeGlobal();
+ const unfixedGlobal = vm.runInNewContext('"use strict"; (0, eval)("this")');
try {
testForBug(unfixedGlobal.Object);
// eslint-disable-next-line no-console

Added tests for unsafeRec