fix(ses): Add test and warning about the `has` hazard
Defer `eval` has check for safe eval
MODIFY
packages/ses/src/scope-handler.js
packages/ses/src/scope-handler.js
@@ -152,9 +152,20 @@ export const createScopeHandler = (
// at the expense of 'typeof' being wrong for those properties. For
// example, in the browser, evaluating 'document = 3', will add
// a property to globalObject instead of throwing a ReferenceError.
+
+ // !!!!! WARNING: DANGER ZONE !!!!!!
+ // The order of the conditions in the `||` expression below is of the
+ // utmost importance. Under no circumstances should `eval` be checked
+ // after `globalObject`. The prototype of the global object is under
+ // full control of user code and may be replaced by a proxy with a
+ // `has` trap. If we allow that trap to trigger while the
+ // `allowNextEvalToBeUnsafe` flag is down, it could allow user code
+ // to get a hold of `FERAL_EVAL`, resulting in a complete escape of
+ // the compartment.
+ // !!!!! WARNING: DANGER ZONE !!!!!!
return (
sloppyGlobalsMode ||
- prop === 'eval' ||
+ (allowNextEvalToBeUnsafe && prop === 'eval') ||
prop in localObject ||
prop in globalObject ||
prop in globalThis

MODIFY
packages/ses/test/test-scope-handler.js
packages/ses/test/test-scope-handler.js
@@ -60,6 +60,40 @@ test('scopeHandler - has trap in sloppyGlobalsMode', t => {
delete globalThis.bar;
});
+test('scopeHandler - has trap guards eval with its life', t => {
+ t.plan(3);
+ let guardDown = false;
+
+ const globalObject = {
+ __proto__: new Proxy(
+ {},
+ {
+ has(...args) {
+ if (args[1] === 'eval' && guardDown) {
+ t.fail('Scope Handler let globalObject catch eval');
+ }
+ return Reflect.has(...args);
+ },
+ },
+ ),
+ };
+
+ const {
+ scopeHandler: handler,
+ resetOneUnsafeEvalNext,
+ admitOneUnsafeEvalNext,
+ } = createScopeHandler(globalObject);
+
+ admitOneUnsafeEvalNext();
+ guardDown = true;
+ t.is(handler.has(null, 'eval'), true);
+ t.is(handler.get(null, 'eval'), FERAL_EVAL);
+
+ resetOneUnsafeEvalNext();
+ guardDown = false;
+ t.is(handler.get(null, 'eval'), undefined);
+});
+
test('scopeHandler - get trap', t => {
t.plan(7);

fix(ses): Add test and warning about the `has` hazard
Defer `eval` has check for safe eval