Skip to content

Commit 7a31e94

Browse files
committed
feat(ses,pass-style): sham use of sham non-trapping for cross-repo testing
1 parent d9ec3dd commit 7a31e94

File tree

10 files changed

+103
-29
lines changed

10 files changed

+103
-29
lines changed

packages/captp/src/captp.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const reverseSlot = slot => {
4747
};
4848

4949
/**
50-
* @typedef {object} CapTPImportExportTables
50+
* @typedef {object} CapTPImportExportTables
5151
* @property {(value: any) => CapTPSlot} makeSlotForValue
5252
* @property {(slot: CapTPSlot, iface: string | undefined) => any} makeValueForSlot
5353
* @property {(slot: CapTPSlot) => boolean} hasImport
@@ -58,12 +58,12 @@ const reverseSlot = slot => {
5858
* @property {(slot: CapTPSlot, value: any) => void} markAsExported
5959
* @property {(slot: CapTPSlot) => void} deleteExport
6060
* @property {() => void} didDisconnect
61-
61+
*
6262
* @typedef {object} MakeCapTPImportExportTablesOptions
6363
* @property {boolean} gcImports
6464
* @property {(slot: CapTPSlot) => void} releaseSlot
6565
* @property {(slot: CapTPSlot) => RemoteKit} makeRemoteKit
66-
66+
*
6767
* @param {MakeCapTPImportExportTablesOptions} options
6868
* @returns {CapTPImportExportTables}
6969
*/

packages/marshal/src/encodeToCapData.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const {
3131
entries,
3232
fromEntries,
3333
freeze,
34+
// @ts-expect-error TS doesn't see this on ObjectConstructor
35+
suppressTrapping = freeze,
3436
} = Object;
3537

3638
/**
@@ -176,10 +178,10 @@ export const makeEncodeToCapData = (encodeOptions = {}) => {
176178
// We harden the entire capData encoding before we return it.
177179
// `encodeToCapData` requires that its input be Passable, and
178180
// therefore hardened.
179-
// The `freeze` here is needed anyway, because the `rest` is
181+
// The `suppressTrapping` here is needed anyway, because the `rest` is
180182
// freshly constructed by the `...` above, and we're using it
181183
// as imput in another call to `encodeToCapData`.
182-
result.rest = encodeToCapDataRecur(freeze(rest));
184+
result.rest = encodeToCapDataRecur(suppressTrapping(rest));
183185
}
184186
return result;
185187
}

packages/pass-style/src/passStyle-helpers.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ const {
1111
getOwnPropertyDescriptor,
1212
getPrototypeOf,
1313
hasOwnProperty: objectHasOwnProperty,
14-
isFrozen,
1514
prototype: objectPrototype,
15+
isFrozen,
16+
// @ts-expect-error TS does not yet have `isNonTrapping` on ObjectConstructor
17+
isNonTrapping = isFrozen,
1618
} = Object;
1719
const { apply } = Reflect;
1820
const { toStringTag: toStringTagSymbol } = Symbol;
@@ -167,6 +169,9 @@ const makeCheckTagRecord = checkProto => {
167169
CX(check)`A non-object cannot be a tagRecord: ${tagRecord}`)) &&
168170
(isFrozen(tagRecord) ||
169171
(!!check && CX(check)`A tagRecord must be frozen: ${tagRecord}`)) &&
172+
(isNonTrapping(tagRecord) ||
173+
(!!check &&
174+
CX(check)`A tagRecord must be non-trapping: ${tagRecord}`)) &&
170175
(!isArray(tagRecord) ||
171176
(!!check && CX(check)`An array cannot be a tagRecord: ${tagRecord}`)) &&
172177
checkPassStyle(

packages/pass-style/src/passStyleOf.js

+24-11
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ import { assertPassableString } from './string.js';
3131
/** @typedef {Exclude<PassStyle, PrimitiveStyle | "promise">} HelperPassStyle */
3232

3333
const { ownKeys } = Reflect;
34-
const { isFrozen, getOwnPropertyDescriptors, values } = Object;
34+
const {
35+
getOwnPropertyDescriptors,
36+
values,
37+
isFrozen,
38+
// @ts-expect-error TS does not yet have `isNonTrapping` on ObjectConstructor
39+
isNonTrapping = isFrozen,
40+
} = Object;
3541

3642
/**
3743
* @param {PassStyleHelper[]} passStyleHelpers
@@ -143,14 +149,17 @@ const makePassStyleOf = passStyleHelpers => {
143149
if (inner === null) {
144150
return 'null';
145151
}
146-
if (!isFrozen(inner)) {
147-
assert.fail(
148-
// TypedArrays get special treatment in harden()
149-
// and a corresponding special error message here.
150-
isTypedArray(inner)
151-
? X`Cannot pass mutable typed arrays like ${inner}.`
152-
: X`Cannot pass non-frozen objects like ${inner}. Use harden()`,
153-
);
152+
if (!isNonTrapping(inner)) {
153+
if (!isFrozen(inner)) {
154+
throw assert.fail(
155+
// TypedArrays get special treatment in harden()
156+
// and a corresponding special error message here.
157+
isTypedArray(inner)
158+
? X`Cannot pass mutable typed arrays like ${inner}.`
159+
: X`Cannot pass non-frozen objects like ${inner}. Use harden()`,
160+
);
161+
}
162+
throw Fail`Cannot pass trapping objects like ${inner}`;
154163
}
155164
if (isPromise(inner)) {
156165
assertSafePromise(inner);
@@ -177,8 +186,12 @@ const makePassStyleOf = passStyleHelpers => {
177186
return 'remotable';
178187
}
179188
case 'function': {
180-
isFrozen(inner) ||
181-
Fail`Cannot pass non-frozen objects like ${inner}. Use harden()`;
189+
if (!isNonTrapping(inner)) {
190+
if (!isFrozen(inner)) {
191+
throw Fail`Cannot pass non-frozen objects like ${inner}. Use harden()`;
192+
}
193+
throw Fail`Cannot pass trapping objects like ${inner}. Use harden()`;
194+
}
182195
typeof inner.then !== 'function' ||
183196
Fail`Cannot pass non-promise thenables`;
184197
remotableHelper.assertValid(inner, passStyleOfRecur);

packages/pass-style/src/remotable.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ const { ownKeys } = Reflect;
2424
const { isArray } = Array;
2525
const {
2626
getPrototypeOf,
27-
isFrozen,
2827
prototype: objectPrototype,
2928
getOwnPropertyDescriptors,
29+
isFrozen,
30+
// @ts-expect-error TS does not yet have `isNonTrapping` on ObjectConstructor
31+
isNonTrapping = isFrozen,
3032
} = Object;
3133

3234
/**
@@ -154,10 +156,13 @@ const checkRemotable = (val, check) => {
154156
if (confirmedRemotables.has(val)) {
155157
return true;
156158
}
157-
if (!isFrozen(val)) {
158-
return (
159-
!!check && CX(check)`cannot serialize non-frozen objects like ${val}`
160-
);
159+
if (!isNonTrapping(val)) {
160+
if (!isFrozen(val)) {
161+
return (
162+
!!check && CX(check)`cannot serialize non-frozen objects like ${val}`
163+
);
164+
}
165+
return !!check && CX(check)`cannot serialize trapping objects like ${val}`;
161166
}
162167
// eslint-disable-next-line no-use-before-define
163168
if (!RemotableHelper.canBeValid(val, check)) {

packages/pass-style/src/safe-promise.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ import { assertChecker, hasOwnPropertyOf, CX } from './passStyle-helpers.js';
66

77
/** @import {Checker} from './types.js' */
88

9-
const { isFrozen, getPrototypeOf, getOwnPropertyDescriptor } = Object;
9+
const {
10+
getPrototypeOf,
11+
getOwnPropertyDescriptor,
12+
isFrozen,
13+
// @ts-expect-error TS does not yet have `isNonTrapping` on ObjectConstructor
14+
isNonTrapping = isFrozen,
15+
} = Object;
1016
const { ownKeys } = Reflect;
1117
const { toStringTag } = Symbol;
1218

@@ -88,7 +94,7 @@ const checkPromiseOwnKeys = (pr, check) => {
8894
if (
8995
typeof val === 'object' &&
9096
val !== null &&
91-
isFrozen(val) &&
97+
isNonTrapping(val) &&
9298
getPrototypeOf(val) === Object.prototype
9399
) {
94100
const subKeys = ownKeys(val);
@@ -133,6 +139,7 @@ const checkPromiseOwnKeys = (pr, check) => {
133139
const checkSafePromise = (pr, check) => {
134140
return (
135141
(isFrozen(pr) || CX(check)`${pr} - Must be frozen`) &&
142+
(isNonTrapping(pr) || CX(check)`${pr} - Must be non-trapping`) &&
136143
(isPromise(pr) || CX(check)`${pr} - Must be a promise`) &&
137144
(getPrototypeOf(pr) === Promise.prototype ||
138145
CX(check)`${pr} - Must inherit from Promise.prototype: ${q(

packages/pass-style/test/passStyleOf.test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const harden = /** @type {import('ses').Harden & { isFake?: boolean }} */ (
1313
global.harden
1414
);
1515

16-
const { getPrototypeOf, defineProperty, freeze } = Object;
16+
const { getPrototypeOf, defineProperty, suppressTrapping } = Object;
1717
const { ownKeys } = Reflect;
1818

1919
test('passStyleOf basic success cases', t => {
@@ -208,7 +208,7 @@ test('passStyleOf testing remotables', t => {
208208
*
209209
* @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385
210210
*/
211-
const farObj2 = freeze({ __proto__: tagRecord2 });
211+
const farObj2 = suppressTrapping({ __proto__: tagRecord2 });
212212
if (harden.isFake) {
213213
t.is(passStyleOf(farObj2), 'remotable');
214214
} else {
@@ -386,7 +386,7 @@ test('remotables - safety from the gibson042 attack', t => {
386386
* explicitly make this non-trapping, which we cannot yet express.
387387
* @see https://github.com/endojs/endo/blob/master/packages/ses/docs/preparing-for-stabilize.md
388388
*/
389-
const makeInput = () => freeze({ __proto__: mercurialProto });
389+
const makeInput = () => suppressTrapping({ __proto__: mercurialProto });
390390
const input1 = makeInput();
391391
const input2 = makeInput();
392392

packages/ses/src/commons.js

+22
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
/* global globalThis */
1515
/* eslint-disable no-restricted-globals */
1616

17+
import './sham-non-trapping/non-trapping-shim.js';
18+
1719
// We cannot use globalThis as the local name since it would capture the
1820
// lexical name.
1921
const universalThis = globalThis;
@@ -75,8 +77,25 @@ export const {
7577
setPrototypeOf,
7678
values,
7779
fromEntries,
80+
// The following is commented out due to
81+
// https://github.com/endojs/endo/issues/2094
82+
// TODO Once fixed, comment this back in and remove the workaround
83+
// immediately below.
84+
//
85+
// // https://github.com/endojs/endo/pull/2673
86+
// // @ts-expect-error TS does not yet have this on ObjectConstructor.
87+
// isNonTrapping = isFrozen,
88+
// // @ts-expect-error TS does not yet have this on ObjectConstructor.
89+
// suppressTrapping = freeze,
7890
} = Object;
7991

92+
// workaround for https://github.com/endojs/endo/issues/2094
93+
// See commented out code and note immediately above.
94+
// @ts-expect-error TS does not yet have this on ObjectConstructor.
95+
export const isNonTrapping = Object.isNonTrapping || isFrozen;
96+
// @ts-expect-error TS does not yet have this on ObjectConstructor.
97+
export const suppressTrapping = Object.suppressTrapping || freeze;
98+
8099
export const {
81100
species: speciesSymbol,
82101
toStringTag: toStringTagSymbol,
@@ -125,6 +144,9 @@ export const {
125144
ownKeys,
126145
preventExtensions: reflectPreventExtensions,
127146
set: reflectSet,
147+
// https://github.com/endojs/endo/pull/2673
148+
isNonTrapping: reflectIsNonTrapping,
149+
suppressTrapping: reflectSuppressTrapping,
128150
} = Reflect;
129151

130152
export const { isArray, prototype: arrayPrototype } = Array;

packages/ses/src/make-hardener.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import {
3030
apply,
3131
arrayForEach,
3232
defineProperty,
33-
freeze,
3433
getOwnPropertyDescriptor,
3534
getOwnPropertyDescriptors,
3635
getPrototypeOf,
@@ -49,6 +48,8 @@ import {
4948
FERAL_STACK_GETTER,
5049
FERAL_STACK_SETTER,
5150
isError,
51+
isFrozen,
52+
suppressTrapping,
5253
} from './commons.js';
5354
import { assert } from './error/assert.js';
5455

@@ -128,6 +129,10 @@ const freezeTypedArray = array => {
128129
* @returns {Harden}
129130
*/
130131
export const makeHardener = () => {
132+
// TODO Get the native hardener to suppressTrapping at each step,
133+
// rather than freeze. Until then, we cannot use it, which is *expensive*!
134+
// TODO Comment out the following to skip the native hardener.
135+
//
131136
// Use a native hardener if possible.
132137
if (typeof globalThis.harden === 'function') {
133138
const safeHarden = globalThis.harden;
@@ -182,8 +187,17 @@ export const makeHardener = () => {
182187
// Also throws if the object is an ArrayBuffer or any TypedArray.
183188
if (isTypedArray(obj)) {
184189
freezeTypedArray(obj);
190+
if (isFrozen(obj)) {
191+
// After `freezeTypedArray`, the typed array might actually be
192+
// frozen if
193+
// - it has no indexed properties
194+
// - it is backed by an Immutable ArrayBuffer as proposed.
195+
// In either case, this makes it a candidate to be made
196+
// non-trapping.
197+
suppressTrapping(obj);
198+
}
185199
} else {
186-
freeze(obj);
200+
suppressTrapping(obj);
187201
}
188202

189203
// we rely upon certain commitments of Object.freeze and proxies here

packages/ses/src/permits.js

+6
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,9 @@ export const permitted = {
488488
groupBy: fn,
489489
// Seen on QuickJS
490490
__getClass: false,
491+
// https://github.com/endojs/endo/pull/2673
492+
isNonTrapping: fn,
493+
suppressTrapping: fn,
491494
},
492495

493496
'%ObjectPrototype%': {
@@ -1624,6 +1627,9 @@ export const permitted = {
16241627
set: fn,
16251628
setPrototypeOf: fn,
16261629
'@@toStringTag': 'string',
1630+
// https://github.com/endojs/endo/pull/2673
1631+
isNonTrapping: fn,
1632+
suppressTrapping: fn,
16271633
},
16281634

16291635
Proxy: {

0 commit comments

Comments
 (0)