-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ses): Add XS variant of shim #2471
Conversation
8ddbfc3
to
70beef1
Compare
5e2a457
to
edb5c78
Compare
70beef1
to
5342ea3
Compare
edb5c78
to
08138db
Compare
5342ea3
to
607b4ee
Compare
08138db
to
a4f3663
Compare
607b4ee
to
a02438f
Compare
a4f3663
to
920ddd8
Compare
a02438f
to
82a89a8
Compare
920ddd8
to
ae04c9e
Compare
82a89a8
to
84a2fb7
Compare
ae04c9e
to
f2e4a22
Compare
84a2fb7
to
c1cdb57
Compare
f2e4a22
to
aca33ea
Compare
c1cdb57
to
7312722
Compare
aca33ea
to
47fa5f2
Compare
7312722
to
f8dd207
Compare
47fa5f2
to
10ef65c
Compare
f8dd207
to
13eb35a
Compare
10ef65c
to
b45a4e0
Compare
13eb35a
to
9f9b0ae
Compare
b45a4e0
to
63ce6b4
Compare
9f9b0ae
to
f6a6bac
Compare
63ce6b4
to
8d472d9
Compare
f6a6bac
to
69152ea
Compare
8d472d9
to
dff1b9c
Compare
3417669
to
6d709e7
Compare
fa10b9b
to
9e16181
Compare
packages/ses/src-xs/index.js
Outdated
const modules = delegateNative | ||
? fromEntries( | ||
arrayMap( | ||
entries(options.modules ?? {}), | ||
([specifier, descriptor]) => [ | ||
specifier, | ||
adaptModuleDescriptor(descriptor, specifier, undefined), | ||
], | ||
), | ||
) | ||
: {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add comment explaining that this module
is only used for nativeOptions
and the shim options pass options.modules
thru unaltered. Arguably, the {}
at the end could be anything, but {}
satisfies the type.
packages/import-bundle/NEWS.md
Outdated
Experimental: | ||
|
||
- Adds a `__native__: true` option that indicates that the application will | ||
fall through to the native implementation of Compartment, currently only | ||
available on XS, which lacks support for precompiled module sources (as exist | ||
in many archived applications, particularly Agoric smart contract bundles) | ||
and instead supports loading modules from original sources (which is not | ||
possible at runtime on XS). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternative design that I am interested in considering is to instead create a new moduleFormat
called endoNativeZipBase64
that implies this __native__
flag. Whether it can be overridden is worth consideration. This would imply a change in bundle-source
such that the existing --no-transforms
or a new --native
flags would switch the moduleFormat
. We can create bundles with -T,--no-transforms
today that cannot be imported without this __native__
flag on XS, but once this PR lands, can be imported without the __native__
flag on Node.js.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand
loading modules from original sources (which is not possible at runtime on XS)
What about this is not possible on XS?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SES on XS, both before this change and after this change (but without the __native__
option set), cannot load a module from original sources without the assistance of the optional ModuleSource
shim.
In any case, this prose needs some work, but I’ve also moved this commit out of this PR since I intend to investigate the alternate design using an "endoNativeZipBase64"
format. The MetaMask folks reminded me that the web will still benefit from pre-compiled bundles, so those should remain the default and we should refrain from changing the default even with a major version bump on bundle-source
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review so far
packages/compartment-mapper/NEWS.md
Outdated
Experimental: | ||
|
||
- Adds a `__native__: true` option to all paths to import, that indicates that | ||
the application will fall through to the native implementation of | ||
Compartment, currently only available on XS, which lacks support for | ||
precompiled module sources (as exist in many archived applications, | ||
particularly Agoric smart contract bundles) and instead supports loading | ||
modules from original sources (which is not possible at runtime on XS). | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to include this under "v1.3.0" rather than "Next version"?
packages/import-bundle/NEWS.md
Outdated
Experimental: | ||
|
||
- Adds a `__native__: true` option that indicates that the application will | ||
fall through to the native implementation of Compartment, currently only | ||
available on XS, which lacks support for precompiled module sources (as exist | ||
in many archived applications, particularly Agoric smart contract bundles) | ||
and instead supports loading modules from original sources (which is not | ||
possible at runtime on XS). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand
loading modules from original sources (which is not possible at runtime on XS)
What about this is not possible on XS?
packages/ses/NEWS.md
Outdated
|
||
Incubating: Please do not rely on these features as they are under development | ||
and subject to breaking changes that will not be signaled by semver. | ||
|
||
- Adds permits for `ModuleSource`, either the native implementation or from | ||
`@endo/module-source/shim.js`. | ||
- Adds support for an XS-specific variant of the SES shim that is triggered | ||
with the `xs` package export condition. | ||
This version of SES preserves all the features of `Compartment` provided | ||
uniquely by the SES shim, but with the `__native__` constructor option, | ||
loses support for importing precompiled module records and gains support | ||
for native `ModuleSource`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you truly want to add/change an entry in an older NEWS section, could you add something to the "Next release" section to make clear what is new?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unintentional: moved up.
throw Error( | ||
`Cannot bundle: encountered deferredError ${deferredError}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep the errors connected re causal console log output
throw Error( | |
`Cannot bundle: encountered deferredError ${deferredError}`, | |
Fail`Cannot bundle: encountered deferredError ${deferredError}`, |
Also, consider for other asserts. Unless we're trying to avoid our assertion support and style in this package for some reason. Are we? Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-raising @erights's comment:
Keep the errors connected re causal console log output
Suggested change
throw Error( `Cannot bundle: encountered deferredError ${deferredError}`, Fail`Cannot bundle: encountered deferredError ${deferredError}`, Also, consider for other asserts. Unless we're trying to avoid our assertion support and style in this package for some reason. Are we? Why?
packages/ses/src-xs/index.js
Outdated
import '../src/assert-shim.js'; | ||
import '../src/console-shim.js'; | ||
import { repairIntrinsics } from '../src/lockdown.js'; | ||
|
||
import { | ||
makeCompartmentConstructor as makeShimCompartmentConstructor, | ||
compartmentOptions, | ||
} from '../src/compartment.js'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor:
This file is intended to be an xs-flavored alternative to ses/index.js. Two minor changes would help a reader see and understand the parallelism:
- Move ses/index.js to ses/src/index.js, leaving behind a forwarding stub if appropriate.
- Rearrange the import order in that file to
so the reader doesn't need to worry about whether the difference in import order is significant.
import './assert-shim.js'; import './console-shim.js'; import './lockdown-shim.js'; import './compartment-shim.js';
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might it even make sense to break this file up into a ses/src-xs/lockdown-shim.js
and ses/src-xs/compartment-shim.js
, and then change ses/src-xs/index.js
to be that much more similar?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve taken this advice. You will find more files in src-xs
on your next review.
packages/ses/src-xs/index.js
Outdated
); | ||
|
||
globalThis.lockdown = options => { | ||
const hardenIntrinsics = repairIntrinsics(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I notice that this PR does not modify lockdown.js
which defines the repairIntrinsics
being called here. Nor does it modify tame-harden.js
or make-hardener.js
used by repairIntrinsics
to make the harden
function. But presumably, on XS we want to use only the XS native harden
. How does that happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use native harden due to an existing fall through to globalThis.harden in makeHarden
. We also continue to respect the lockdown option to make harden
a no-op. I’ve elected to continue using our lockdown
variant so-far, since it is parameterized, can conduct repairs to the XS intrinsics if any become necessary, and support enablements. I’m at least leaving native lockdown out of scope for this run at native Compartment and ModuleSource.
packages/ses/src-xs/index.js
Outdated
hardenIntrinsics(); | ||
// Replace global Compartment with a version that is hardened and hardens | ||
// transitive child Compartment. | ||
globalThis.Compartment = FERAL_EVAL(compartmentShim)( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This both reevaluates compartmentShim
and reapplies its completion value, which is the big function defined by inline text above, to similar arguments, with the main (only?) difference being noHarden
vs harden
. While this is all fine, so no change necessarily suggested, it does make me wonder whether we could evaluate the function only once and have all needed differences come only from the application arguments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve posted a change that eliminates the eval entirely. It’s now just a function adaptCompartmentConstructors(NativeCompartmentConstructor, ShimCompartmentConstructor, maybeHarden)
. I look forward to possibly remembering why the eval was necessary, but it probably wasn’t actually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review complete on everything but src-xs/index.js
This looks good to me in broad outline, but I have not yet found the time to review it in detail. @gibson042 , can you complete this? If you approve this one file, I'm willing to approve the whole thing. Thanks!
9e16181
to
53e5109
Compare
6d709e7
to
f388ca5
Compare
b8149b7
to
52598c1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems nicely threaded to me.
packages/compartment-mapper/NEWS.md
Outdated
@@ -23,13 +23,43 @@ User-visible changes to `@endo/compartment-mapper`: | |||
originally intended. To those who expected the previous behavior: if you | |||
exist, please exercise caution when upgrading. | |||
|
|||
Experimental: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be moved up to a "Next version" above v1.4.0?
throw Error( | ||
`Cannot bundle: encountered deferredError ${deferredError}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-raising @erights's comment:
Keep the errors connected re causal console log output
Suggested change
throw Error( `Cannot bundle: encountered deferredError ${deferredError}`, Fail`Cannot bundle: encountered deferredError ${deferredError}`, Also, consider for other asserts. Unless we're trying to avoid our assertion support and style in this package for some reason. Are we? Why?
packages/ses/src-xs/commons.js
Outdated
uncurryThis, | ||
} from '../src/commons.js'; | ||
|
||
export const NativeStartCompartment = globalThis.Compartment; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not NativeCompartment
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making the Start distinction avoids a shadowing problem elsewhere, when a child compartment has its own constructors.
for (const transform of transforms) { | ||
source = transform(source); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do the imports from commons.js mean that this code needs to be robust against a tampered environment? If so, then it probably shouldn't use of
.
for (const transform of transforms) { | |
source = transform(source); | |
} | |
for (let i = 0; i < transforms.length; i += 1) { | |
source = transforms[i](source); | |
} |
packages/ses/src-xs/compartment.js
Outdated
? fromEntries( | ||
arrayMap( | ||
entries(options.modules ?? {}), | ||
([specifier, descriptor]) => [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do the imports from commons.js mean that this code needs to be robust against a tampered environment? If so, then
([specifier, descriptor]) => [ | |
({ 0: specifier, 1: descriptor }) => [ |
packages/ses/src-xs/compartment.js
Outdated
} | ||
// eslint-disable-next-line no-underscore-dangle | ||
if (source.__syncModuleProgram__) { | ||
throw new SyntaxError( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure @erights prefers universal avoidance of new
with built-in error constructors.
packages/ses/src-xs/index.js
Outdated
// We disable this behavior to encourage use of `harden` for portable Hardened | ||
// JavaScript. | ||
/** @param {object} object */ | ||
Object.freeze = object => freeze(object); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's maintain Object.freeze.name
.
Object.freeze = object => freeze(object); | |
Object.freeze = ({ freeze: object => freeze(object) }).freeze; |
@@ -0,0 +1,26 @@ | |||
/** | |||
* @module Alters the XS implementation of Lockdown to be backward compatible |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @module Alters the XS implementation of Lockdown to be backward compatible | |
* @module Alters the XS implementation of lockdown to be backward compatible |
packages/ses/src-xs/lockdown-shim.js
Outdated
globalThis.lockdown = options => { | ||
const hardenIntrinsics = repairIntrinsics(options); | ||
hardenIntrinsics(); | ||
// Replace global Compartment with a version that is hardened and hardens | ||
// transitive child Compartment. | ||
globalThis.Compartment = adaptCompartmentConstructors( | ||
NativeStartCompartment, | ||
ShimStartCompartment, | ||
harden, | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
globalThis.lockdown = options => { | |
const hardenIntrinsics = repairIntrinsics(options); | |
hardenIntrinsics(); | |
// Replace global Compartment with a version that is hardened and hardens | |
// transitive child Compartment. | |
globalThis.Compartment = adaptCompartmentConstructors( | |
NativeStartCompartment, | |
ShimStartCompartment, | |
harden, | |
); | |
}; | |
const lockdown = options => { | |
const hardenIntrinsics = repairIntrinsics(options); | |
hardenIntrinsics(); | |
// Replace global Compartment with a version that is hardened and hardens | |
// transitive child Compartment. | |
globalThis.Compartment = adaptCompartmentConstructors( | |
NativeStartCompartment, | |
ShimStartCompartment, | |
harden, | |
); | |
}; | |
globalThis.lockdown = lockdown; |
c67795d
to
da44206
Compare
da44206
to
bd79347
Compare
Closes: #2251
Description
To take advantage of native XS compartments while retaining backward compatibility and parity with the SES shim, we introduce an
xs
specific Compartment adapter that requires two levels of opt-in.xs
package export/import condition.__native__
option to sacrifice the ability to use precompiled module sources (as generated by@endo/module-source
without thexs
package export/import condition) and instead use adapted nativeModuleSource
(as generated by@endo/module-source
with thexs
package export/import condition). This allows@endo/import-bundle
, for example, to use the JSON serialization of a precompiled module source that is captured in a bundle and also opt-in for__native__
treatment if the archive/bundle contains original sources.This change introduces an XS-specific shim that is an adapter for
Compartment
andlockdown
, and also papers over parity gaps like the XSObject.freeze
second boolean argument. The adapter creates parallel native and shim (virtual) compartment trees, where any individual Compartment can elect to use the native or shim variant for a child Compartment.We have not yet found a workable design that obviates the need for the
__native__
opt-in. Such a design would need to create an adapter from precompiled module sources to XS’s virtual module source protocol. To do that would require native module to emit notifications for the mutation of exported live bindings and also require the native Compartment evaluate method to accept an argument like the shim’s__moduleGlobalLexicals__
.Security Considerations
Uncountably numerous. Among them, with the
__native__
option, censorship does not occur, so dynamic import and direct eval are possible.Scaling Considerations
The native ModuleSource makes it practical to defer module parsing to runtime, and should improve the performance of execution as well.
Documentation Considerations
The NEWS.md qualifies these changes as under "incubation". When the shape of these changes settles, the NEWS will need to reiterate the final user facing API in README.md and NEWS.md. With the
__native__
option, censorship does not occur, so dynamic import and direct eval are possible.Testing Considerations
This change contains a token of
xst
testing that is exercised in CI withtest:xs
. This demonstrates the use of@endo/compartment-mapper/bundle.js
to thread thexs
package export/import condition and generate a script that canxst
can run directly. This gives us some modest confidence that lockdown works and demonstrates the__native__
feature but does not provide sufficient confidence of parity for the gamut of Compartment usage, both legacy and XS, for all of the accepted module descriptors and other Compartment features. This is an exercise that will begin with a subsequent change that introduceshardened262
, a comprehensive parity checking framework for the full cross-product of [ SES on Node.js, SES on XS, and XS stand-alone ] ⨉ [ Lockdown, not Lockdown ] ⨉ [ Compartment, no Compartment ] ⨉ [ Sloppy, Strict, Module ].Compatibility Considerations
This change preserves all existing usage and introduces an unstable alternate version of SES for XS that requires two layers of opt-in. The use of the
xs
condition introduces an adapter forCompartment
that may not have full parity with the underlying implementation, and requires additional testing. The__native__
option elects to break some usage (precompiled moduels) in favor of others (dynamic import, direct eval, top-level-await).Upgrade Considerations
In order to realize these changes on the Agoric chain will likely require a more recent version of XS and switching the bundle format for the lockdown/bootstrap script for xsnap swingset workers.