From 1d4bd35424d312e864109378f0ee0c8bab4f9ced Mon Sep 17 00:00:00 2001 From: LeoTM <1881059+leotm@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:53:53 +0000 Subject: [PATCH 1/2] fix(ses): expect fn instance prototype on Hermes in permit --- packages/ses/src/permits.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/ses/src/permits.js b/packages/ses/src/permits.js index fa62aaa7ea..e842f43f7a 100644 --- a/packages/ses/src/permits.js +++ b/packages/ses/src/permits.js @@ -270,6 +270,9 @@ export const FunctionInstance = { // Do not specify "prototype" here, since only Function instances that can // be used as a constructor have a prototype property. For constructors, // since prototype properties are instance-specific, we define it there. + // Older versions of Hermes contain a non-standard prototype, noted below in 'hermesFn'. + // This is fixed fully in Static Hermes: https://github.com/facebook/hermes/tree/static_h + // See: https://speakerdeck.com/tmikov2023/optimizing-with-static-hermes-chain-react-2024 }; // AsyncFunction Instances @@ -281,6 +284,14 @@ export const AsyncFunctionInstance = { // Aliases const fn = FunctionInstance; +// Bypass Hermes bugs, fixed in: +// - https://github.com/facebook/hermes/commit/c42491de94aff479e5e83c073eff96a6261da080 (hermes: v0.13.0) +// - https://github.com/facebook/hermes/commit/00f18c89c720e1c34592bb85a1a8d311e6e99599 (sh_stable, static_h) +// Expect Additional Properties of the Global Object (Annex B) proposed by SES, +// that are function instances defined as arrow functions ('lockdown' and 'harden') +// and concise methods ('%InitialGetStackString%'), to have their non-standard +// prototype properties set to `undefined` in intrinsics.js when completing prototypes. +const hermesFn = { ...FunctionInstance, prototype: 'undefined' }; const asyncFn = AsyncFunctionInstance; const getter = { @@ -1643,8 +1654,8 @@ export const permitted = { '@@toStringTag': 'string', }, - lockdown: fn, - harden: { ...fn, isFake: 'boolean' }, + lockdown: hermesFn, + harden: { ...hermesFn, isFake: 'boolean' }, - '%InitialGetStackString%': fn, + '%InitialGetStackString%': hermesFn, }; From 048efd377d6ed0714251d9b6ff81a1c6620b8995 Mon Sep 17 00:00:00 2001 From: LeoTM <1881059+leotm@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:54:07 +0000 Subject: [PATCH 2/2] fix(ses): set fn instance prototype to undefined on Hermes --- packages/ses/src/intrinsics.js | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/packages/ses/src/intrinsics.js b/packages/ses/src/intrinsics.js index 4355d0c6ab..c74697f725 100644 --- a/packages/ses/src/intrinsics.js +++ b/packages/ses/src/intrinsics.js @@ -98,25 +98,40 @@ export const makeIntrinsicsCollector = () => { if (typeof permit !== 'object') { throw TypeError(`Expected permit object at whitelist.${name}`); } - const namePrototype = permit.prototype; - if (!namePrototype) { + const permitPrototype = permit.prototype; + + if ( + typeof intrinsic === 'function' && + intrinsic.prototype !== undefined && + permitPrototype === 'undefined' // permits.js + ) { + // Set non-standard `.prototype` properties to `undefined` on Hermes. + // These include intrinsics that are additional properties of the global object, + // proposed by SES defined as function instances + // - arrow functions: lockdown, harden + // - concise methods: %InitialGetStackString% + intrinsic.prototype = undefined; + } + + const intrinsicPrototype = intrinsic.prototype; + + if (!permitPrototype) { throw TypeError(`${name}.prototype property not whitelisted`); } if ( - typeof namePrototype !== 'string' || - !objectHasOwnProperty(permitted, namePrototype) + typeof permitPrototype !== 'string' || + !objectHasOwnProperty(permitted, permitPrototype) ) { throw TypeError(`Unrecognized ${name}.prototype whitelist entry`); } - const intrinsicPrototype = intrinsic.prototype; - if (objectHasOwnProperty(intrinsics, namePrototype)) { - if (intrinsics[namePrototype] !== intrinsicPrototype) { - throw TypeError(`Conflicting bindings of ${namePrototype}`); + if (objectHasOwnProperty(intrinsics, permitPrototype)) { + if (intrinsics[permitPrototype] !== intrinsicPrototype) { + throw TypeError(`Conflicting bindings of ${permitPrototype}`); } // eslint-disable-next-line no-continue continue; } - intrinsics[namePrototype] = intrinsicPrototype; + intrinsics[permitPrototype] = intrinsicPrototype; } }; freeze(completePrototypes);