Rename compartment.global to globalThis
This change brings the shim in compliance with the current proposed specification.
MODIFY
packages/ses/NEWS.md
packages/ses/NEWS.md
@@ -12,7 +12,7 @@ User-visible changes in SES:
* This version decouples lockdown and the Compartment constructor.
The Compartment constructor is now exported by `ses` (was previously only
- available as a property of the global object *after* lockdown).
+ available as a property of `globalThis` *after* lockdown).
The Compartment constructor will also create "privileged" compartments when
constructed before lockdown.

MODIFY
packages/ses/src/compartment-shim.js
packages/ses/src/compartment-shim.js
@@ -145,7 +145,7 @@ export class Compartment {
});
}
- get global() {
+ get globalThis() {
return privateFields.get(this).globalObject;
}
@@ -204,13 +204,10 @@ export class Compartment {
assertModuleHooks(this);
- await load(privateFields, moduleAnalyses, this, specifier);
- const module = this.importNow(specifier);
- // TODO consider revising the specification to use the term `module`
- // instead of `namespace` to be consistent with the `module` method,
- // since this establishes a precedent that the term `module` without any
- // further qualification denotes the module exports namespace.
- return { namespace: module };
+ return load(privateFields, moduleAnalyses, this, specifier).then(() => {
+ const namespace = this.importNow(specifier);
+ return { namespace };
+ });
}
importNow(specifier) {

MODIFY
packages/ses/src/whitelist.js
packages/ses/src/whitelist.js
@@ -58,8 +58,20 @@ export const FunctionInstance = {
// since prototpye properties are instance-specific, we define it there.
};
+// 25.7.4 AsyncFunction Instances
+export const AsyncFunctionInstance = {
+ // This property is not mentioned in ECMA 262, but is present in V8 and
+ // necessary for lockdown to succeed.
+ '**proto**': 'AsyncFunctionPrototype',
+ // 25.7.4.1 length
+ length: 'number',
+ // 25.7.4.2 name
+ name: 'string',
+};
+
// Aliases
const fn = FunctionInstance;
+const asyncFn = AsyncFunctionInstance;
const getter = {
get: fn,
@@ -1532,7 +1544,10 @@ export default {
CompartmentPrototype: {
constructor: 'Compartment',
evaluate: fn,
- global: getter,
+ globalThis: getter,
+ import: asyncFn,
+ importNow: fn,
+ module: fn,
},
ModuleStaticRecord: {

MODIFY
packages/ses/test/break-function-eval.test.js
packages/ses/test/break-function-eval.test.js
@@ -12,7 +12,7 @@ test('function-no-body', t => {
stubFunctionConstructors(sinon);
const c = new Compartment();
- const f1 = new c.global.Function();
+ const f1 = new c.globalThis.Function();
const src = f1.toString();
t.notOk(src.includes('undefined'));
@@ -29,7 +29,7 @@ test('function-injection', t => {
const goodFunc = 'return a+1';
const c = new Compartment();
- const f1 = new c.global.Function('a', goodFunc);
+ const f1 = new c.globalThis.Function('a', goodFunc);
t.equal(f1(5), 6);
// the naive expansion is: '(function(a) {' + evilFunc + '})'
@@ -41,8 +41,11 @@ test('function-injection', t => {
// which becomes: (function(a) {}, this.haha = 666, {})
const evilFunc = '}, this.haha = 666, {';
- t.throws(() => new c.global.Function('a', evilFunc), c.global.SyntaxError);
- t.equal(c.global.haha, undefined);
+ t.throws(
+ () => new c.globalThis.Function('a', evilFunc),
+ c.globalThis.SyntaxError,
+ );
+ t.equal(c.globalThis.haha, undefined);
sinon.restore();
});
@@ -56,11 +59,15 @@ test('function-injection-2', t => {
const c = new Compartment();
let flag = false;
// eslint-disable-next-line func-names
- c.global.target = function() {
+ c.globalThis.target = function() {
flag = true;
};
function check(...args) {
- t.throws(() => c.global.Function(...args), c.global.SyntaxError, args);
+ t.throws(
+ () => c.globalThis.Function(...args),
+ c.globalThis.SyntaxError,
+ args,
+ );
t.equal(flag, false);
}
@@ -128,7 +135,7 @@ test('function-paren-default', t => {
stubFunctionConstructors(sinon);
const c = new Compartment();
- t.equal(c.global.Function('foo, a = new Date(0)', 'return foo')(99), 99);
+ t.equal(c.globalThis.Function('foo, a = new Date(0)', 'return foo')(99), 99);
sinon.restore();
});
@@ -140,7 +147,7 @@ test('function-default-parameters', t => {
stubFunctionConstructors(sinon);
const c = new Compartment();
- t.equal(c.global.Function('a=1', 'return a+1')(), 2);
+ t.equal(c.globalThis.Function('a=1', 'return a+1')(), 2);
sinon.restore();
});
@@ -152,7 +159,7 @@ test('function-rest-parameter', t => {
stubFunctionConstructors(sinon);
const c = new Compartment();
- t.equal(c.global.Function('...rest', 'return rest[1]')(1, 2, 3), 2);
+ t.equal(c.globalThis.Function('...rest', 'return rest[1]')(1, 2, 3), 2);
sinon.restore();
});
@@ -164,7 +171,10 @@ test('function-destructuring-parameters', t => {
stubFunctionConstructors(sinon);
const c = new Compartment();
- t.equal(c.global.Function('{foo, bar}, baz', 'return bar')({ bar: 99 }), 99);
+ t.equal(
+ c.globalThis.Function('{foo, bar}, baz', 'return bar')({ bar: 99 }),
+ 99,
+ );
sinon.restore();
});
@@ -176,10 +186,10 @@ test('function-legitimate-but-weird-parameters', t => {
stubFunctionConstructors(sinon);
const c = new Compartment();
- const f1 = c.global.Function('foo, bar', 'baz', 'return foo + bar + baz');
+ const f1 = c.globalThis.Function('foo, bar', 'baz', 'return foo + bar + baz');
t.equal(f1(1, 2, 3), 6);
- const f2 = c.global.Function(
+ const f2 = c.globalThis.Function(
'foo, bar = [1',
'2]',
'return foo + bar[0] + bar[1]',
@@ -198,8 +208,8 @@ test('degenerate-pattern-match-argument', t => {
const c = new Compartment();
// This syntax is also rejected by the normal JS parser.
t.throws(
- () => new c.global.Function('3', 'return foo + bar + baz'),
- c.global.SyntaxError,
+ () => new c.globalThis.Function('3', 'return foo + bar + baz'),
+ c.globalThis.SyntaxError,
);
sinon.restore();
@@ -213,13 +223,13 @@ test('frozen-eval', t => {
const c = new Compartment();
- Object.defineProperty(c.global, 'eval', {
- value: c.global.eval,
+ Object.defineProperty(c.globalThis, 'eval', {
+ value: c.globalThis.eval,
writable: false,
configurable: false,
});
- c.global.foo = 77;
+ c.globalThis.foo = 77;
globalThis.foo = 88;
t.equal(c.evaluate('(0,eval)("foo")'), 77);

MODIFY
packages/ses/test/compartment-instance.test.js
packages/ses/test/compartment-instance.test.js
@@ -44,7 +44,7 @@ test('Compartment instance', t => {
'import',
'importNow',
'module',
- 'global',
+ 'globalThis',
'toString',
].sort(),
'prototype properties',

MODIFY
packages/ses/test/compartment-prototype.test.js
packages/ses/test/compartment-prototype.test.js
@@ -20,7 +20,7 @@ test('Compartment prototype', t => {
'import',
'importNow',
'module',
- 'global',
+ 'globalThis',
'toString',
].sort(),
'prototype properties',

MODIFY
packages/ses/test/compartment.test.js
packages/ses/test/compartment.test.js
@@ -108,7 +108,7 @@ test('main use case', t => {
endowments: { power: attenuatedPower },
});
t.equal(user(1), 2);
- t.throws(() => user(-1), c.global.TypeError);
+ t.throws(() => user(-1), c.globalThis.TypeError);
t.end();
});

MODIFY
packages/ses/test/confinement.test.js
packages/ses/test/confinement.test.js
@@ -73,8 +73,8 @@ test('confinement evaluation eval', t => {
const c = new Compartment();
// Strict mode
- t.equal(c.evaluate('(0, eval)("this")'), c.global);
- t.equal(c.evaluate('var evil = eval; evil("this")'), c.global);
+ t.equal(c.evaluate('(0, eval)("this")'), c.globalThis);
+ t.equal(c.evaluate('var evil = eval; evil("this")'), c.globalThis);
sinon.restore();
});

MODIFY
packages/ses/test/global-object-properties.test.js
packages/ses/test/global-object-properties.test.js
@@ -15,21 +15,21 @@ test('globalObject properties', t => {
const c = new Compartment();
- t.notEqual(c.global, this);
- t.notEqual(c.global, globalThis);
- t.equal(c.global.globalThis, c.global);
+ t.notEqual(c.globalThis, this);
+ t.notEqual(c.globalThis, globalThis);
+ t.equal(c.globalThis.globalThis, c.globalThis);
- t.equal(c.global.Array, Array);
- t.equal(c.global.Array, globalThis.Array);
+ t.equal(c.globalThis.Array, Array);
+ t.equal(c.globalThis.Array, globalThis.Array);
// eslint-disable-next-line no-eval
- t.notEqual(c.global.eval, eval);
- t.notEqual(c.global.eval, globalThis.eval);
+ t.notEqual(c.globalThis.eval, eval);
+ t.notEqual(c.globalThis.eval, globalThis.eval);
- t.notEqual(c.global.Function, Function);
- t.notEqual(c.global.Function, globalThis.Function);
+ t.notEqual(c.globalThis.Function, Function);
+ t.notEqual(c.globalThis.Function, globalThis.Function);
- t.equal(c.global.Compartment, Compartment);
+ t.equal(c.globalThis.Compartment, Compartment);
delete globalThis.Compartment;
sinon.restore();

MODIFY
packages/ses/test/identity-continuity.test.js
packages/ses/test/identity-continuity.test.js
@@ -15,7 +15,7 @@ test('identity Array', t => {
globalThis.Compartment = Compartment;
const c1 = new Compartment();
- const c2 = new c1.global.Compartment();
+ const c2 = new c1.globalThis.Compartment();
t.equal(c1.evaluate('Array'), Array);
t.equal(c1.evaluate('Array'), c1.evaluate('Array'));
@@ -24,8 +24,8 @@ test('identity Array', t => {
const a2 = c2.evaluate('[]');
t.ok(a2 instanceof Array);
- t.ok(a2 instanceof c1.global.Array);
- t.ok(a2 instanceof c2.global.Array);
+ t.ok(a2 instanceof c1.globalThis.Array);
+ t.ok(a2 instanceof c2.globalThis.Array);
delete globalThis.Compartment;
sinon.restore();
@@ -41,7 +41,7 @@ test('identity Compartment', t => {
globalThis.Compartment = Compartment;
const c1 = new Compartment();
- const c2 = new c1.global.Compartment();
+ const c2 = new c1.globalThis.Compartment();
t.equal(c1.evaluate('Compartment'), Compartment);
t.equal(c1.evaluate('Compartment'), c1.evaluate('Compartment'));
@@ -50,8 +50,8 @@ test('identity Compartment', t => {
const e3 = c2.evaluate('(new Compartment())');
t.ok(e3 instanceof Compartment);
- t.ok(e3 instanceof c1.global.Compartment);
- t.ok(e3 instanceof c2.global.Compartment);
+ t.ok(e3 instanceof c1.globalThis.Compartment);
+ t.ok(e3 instanceof c2.globalThis.Compartment);
delete globalThis.Compartment;
sinon.restore();
@@ -67,7 +67,7 @@ test('identity eval', t => {
globalThis.Compartment = Compartment;
const c1 = new Compartment();
- const c2 = new c1.global.Compartment();
+ const c2 = new c1.globalThis.Compartment();
t.ok(c2.evaluate('eval') instanceof Function);
t.ok(c2.evaluate('eval') instanceof Object);
@@ -94,8 +94,8 @@ test('identity Function', t => {
globalThis.Compartment = Compartment;
const c1 = new Compartment();
- const c2 = new c1.global.Compartment();
- const c3 = new c2.global.Compartment();
+ const c2 = new c1.globalThis.Compartment();
+ const c3 = new c2.globalThis.Compartment();
t.ok(c2.evaluate('Function') instanceof Function);
t.ok(c2.evaluate('Function') instanceof Object);
@@ -108,9 +108,9 @@ test('identity Function', t => {
t.notEqual(c2.evaluate('Function'), c1.evaluate('Function'));
const f2 = c2.evaluate('function x(a, b) { return a+b; }; x');
- t.ok(f2 instanceof c1.global.Function);
- t.ok(f2 instanceof c2.global.Function);
- t.ok(f2 instanceof c3.global.Function);
+ t.ok(f2 instanceof c1.globalThis.Function);
+ t.ok(f2 instanceof c2.globalThis.Function);
+ t.ok(f2 instanceof c3.globalThis.Function);
delete globalThis.Compartment;
sinon.restore();

MODIFY
packages/ses/test/leak-endowments.test.js
packages/ses/test/leak-endowments.test.js
@@ -38,7 +38,7 @@ test('endowments are mutable but not shared between calls to c.evaluate', t => {
// the global is not modified when an endowment shadows it
t.throws(() => c.evaluate(`endow1`), ReferenceError);
- t.equal(c.global.endow1, undefined);
+ t.equal(c.globalThis.endow1, undefined);
// assignment to global works even when an endowment shadows it
t.equal(
@@ -51,7 +51,7 @@ test('endowments are mutable but not shared between calls to c.evaluate', t => {
);
// the modified global is now visible when there is no endowment to shadow it
- t.equal(c.global.endow1, 4);
+ t.equal(c.globalThis.endow1, 4);
t.equal(c.evaluate(`endow1`), 4);
// endowments shadow globals

MODIFY
packages/ses/test/leak-unsafe-eval.test.js
packages/ses/test/leak-unsafe-eval.test.js
@@ -36,7 +36,7 @@ test('HostException in eval revokes unsafeEval', t => {
// eslint-disable-next-line no-empty
} catch (err) {}
- t.equal(endowments.$$eval$$, c.global.eval, "should be realm's eval");
+ t.equal(endowments.$$eval$$, c.globalThis.eval, "should be realm's eval");
t.notEqual(endowments.$$eval$$, globalThis.eval, 'should not be unsafe eval');
sinon.restore();
@@ -73,7 +73,7 @@ test('HostException in Function revokes unsafeEval', t => {
// eslint-disable-next-line no-empty
} catch (err) {}
- t.equal(endowments.$$eval$$, c.global.eval, "should be realm's eval");
+ t.equal(endowments.$$eval$$, c.globalThis.eval, "should be realm's eval");
t.notEqual(endowments.$$eval$$, globalThis.eval, 'should not be unsafe eval');
sinon.restore();

MODIFY
packages/ses/test/ses.test.js
packages/ses/test/ses.test.js
@@ -180,7 +180,7 @@ test('main use case', t => {
endowments: { power: attenuatedPower },
});
t.equal(user(1), 2);
- t.throws(() => user(-1), c.global.TypeError);
+ t.throws(() => user(-1), c.globalThis.TypeError);
t.end();
});

MODIFY
packages/ses/test/typeof.test.js
packages/ses/test/typeof.test.js
@@ -22,6 +22,7 @@ test('typeof', t => {
// TODO: the Compartment currently censors objects from the unsafe global, but
// they appear defined as 'undefined' and don't throw a ReferenceError.
+ // https://github.com/Agoric/SES-shim/issues/309
t.doesNotThrow(() => c.evaluate('global'), ReferenceError);
t.equal(c.evaluate('global'), undefined);
t.equal(c.evaluate('typeof global'), 'undefined');

MODIFY
packages/ses/test/whitelist.test.js
packages/ses/test/whitelist.test.js
@@ -58,12 +58,12 @@ test('do not remove Object.prototype.__proto__', t => {
const c = new Compartment();
t.equal(
c.evaluate('({}).__proto__'),
- c.global.Object.prototype,
+ c.globalThis.Object.prototype,
'({}).__proto__ is accessible',
);
t.equal(
c.evaluate('[].__proto__'),
- c.global.Array.prototype,
+ c.globalThis.Array.prototype,
'[].__proto__ is accessible',
);
t.equal(
@@ -73,7 +73,7 @@ X.prototype.__proto__ = Array.prototype;
const x=new X();
x.slice;
`),
- c.global.Array.prototype.slice,
+ c.globalThis.Array.prototype.slice,
`prototype __proto__ inheritance works`,
);

Rename compartment.global to globalThis (#315)

This change brings the shim in compliance with the current proposed specification.