tc39 / proposal-shadowrealm

ECMAScript Proposal, specs, and reference implementation for Realms
https://tc39.es/proposal-shadowrealm/
1.43k stars 67 forks source link

Let the host create the global object #392

Open Ms2ger opened 9 months ago

Ms2ger commented 9 months ago

Ref https://github.com/whatwg/html/issues/7591

mhofman commented 9 months ago

This change looks like a regression. It seems that the host gains the ability to create a global object with exotic behavior. In particular the main requirement is that all properties must be effectively deletable. I also believe the intent was for the prototype of the global object to be effectively mutable, and that the global object could be successfully frozen. The reason I mention "effectively" is that an exotic object can claim that properties are configurable, yet refuse to actually mutate or delete them.

Ms2ger commented 9 months ago

I'm happy to add restrictions on the host, but would prefer if someone with more background suggested prose for it.

mhofman commented 8 months ago

394 raises an interesting point about discovery of the host added properties. If the host creates the object, it should implement all added properties as own properties.

I think basically the constraint should be that all behavior implemented by the host created global object be observably regular / non-exotic. In this case the object invariants are not sufficient as they provide too much leeway. At which point I'm still curious why we need this change since a host created object with regular object behavior, and a spec created regular object are observably indistinguishable, and can be considered an internal implementation concern.

ByteEater-pl commented 8 months ago

@mhofman, could @ljharb's "Get Intrinsic" Proposal help reconcile these concerns?

We could use it to enumerate the global object's properties in a ShadowRealm and delete all or some of them (because they're required to be configurable, though that requirement would have to be strengthened so that the delibility isn't faked), e.g. just those added by the host.

And hosts with EventTarget wishing to have ShadowRealms' global objects inherit from it would be free to do so.

Jack-Works commented 8 months ago

I oppose to host creating the global object. HostInitializeShadowRealm is enough.

Without userland ShadowRealm API to do the same thing, an exotic global object is unacceptable.

Can you give the reason why the host should take over the creation?

mhofman commented 8 months ago

could @ljharb's "Get Intrinsic" Proposal help reconcile these concerns?

I don't see how that proposal would help. The concern is not with the ability to enumerate globals, but with requiring that the global object has non-exotic behavior.

And hosts with EventTarget wishing to have ShadowRealms' global objects inherit from it would be free to do so.

Why is the host creating the global object required for this? The prototype of a fresh regular object is mutable. Nothing in the original steps says the global object has to inherit from Object.prototype. Also as I mentioned, whether in the actual implementation the host does in fact create the global object is unobservable, as long as the global object can be observed to have no exotic behavior.

Jack-Works commented 8 months ago

The host can set the global object's prototype to EventTarget.prototype in HostInitializeShadowRealm. The host can also attach the required internal slot of EventTarget to make it functional, this is virtualizable since a non-Web implementation can also do that by using their own fake "EventTarget" that accepts the global object as a valid this.

ByteEater-pl commented 8 months ago

@mhofman, you wrote:

If the host creates the object, it should implement all added properties as own properties.

If instead it creates a regular object as global and operates on it in HostInitializeShadowRealm (whereby setting its prototype, not just adding properties, should therefore be mentioned as an example of what's allowed and likely useful), what stops it from setting an exotic object as a prototype? If it does, and "use this hook to add properties to the ShadowRealm's global object" is interpreted (correctly, as we both seem to assume) as including inherited properties (to which the configurability requirement applies), we're back in square one, since the prototype (or any object up in the prototype chain) can:

What I mean by the (actually understated; worse things can happen) latter is, assuming that in the absence of Get Intrinsic (which I therefore mentioned) you'd traverse the prototype chain with getPrototypeOf (on Object or Reflect) and enumerate everything available, some object on the prototype chain may have a custom [[GetPrototypeOf]], which opens possibilities (not prevented by invariants of the essential internal methods) such as returning a different prototype to the enumerating code than in normal operation, sporting or lazily creating an infinite prototype chain, adding properties (even non-configurable ones) to the global object itself (or any ancestor in the prototype chain), throwing, or simply not terminating.

ByteEater-pl commented 8 months ago

Or maybe it's fine to change the prototype of the global object first (it's ordinary, so possible), thereby loosing its EventTarget quality, and then turn to deleting what you don't want on the object itself.

But even then, I believe the requirements of HostInitializeShadowRealm should be strengthened. If it's currently understood that it can set the global object's prototype, it probably can also delete, replace or reconfigure (even make non-configurable) the properties installed by SetDefaultGlobalBindings. In this and other ShadowRealms. And mangle your module graph (outside the ShadowRealm). And make demons fly out of your nose. Yea, I know other host operations aren't usually so carefully limited, but this one is special, security hinges on it.

mhofman commented 8 months ago

what stops it from setting an exotic object as a prototype?

That's fine, as long as the global object remains extensible, the prototype object can be substituted, which is sufficient.

including inherited properties (to which the configurability requirement applies)

The configurability requirement does not apply to the prototype chain, only to the global object.

you'd traverse the prototype chain with getPrototypeOf (on Object or Reflect) and enumerate everything available

Nope, you'd likely replace the prototype of the global object with a wrapper or other object that behaves as expected.

Or maybe it's fine to change the prototype of the global object first (it's ordinary, so possible), thereby loosing its EventTarget quality, and then turn to deleting what you don't want on the object itself.

Right, but the user doesn't have to lose the EventTarget methods, that prototype can be inserted in the chain, or a wrapper for it, etc.

But even then, I believe the requirements of HostInitializeShadowRealm should be strengthened.

even make non-configurable) the properties installed by SetDefaultGlobalBindings

I agree, HostInitializeShadowRealm should not be allowed to add or make any properties of the global object non-configurable, nor should it be allowed to make the global object non-extensible.

In this and other ShadowRealms

Yes, I wanted to have some text somewhere that prevented the host from making properties appear or otherwise change properties on the global object after HostInitializeShadowRealm, but I can't track down where I asked for this.

And mangle your module graph (outside the ShadowRealm). And make demons fly out of your nose. Yea, I know other host operations aren't usually so carefully limited, but this one is special, security hinges on it.

I honestly don't care about what the host does outside the shadow realm after its creation, as long as it respects the callable boundary invariants (which are now spelled out precisely) and the above requirements to not further change the global object after initialization.

Jack-Works commented 8 months ago

we're back in square one, since the prototype (or any object up in the prototype chain) can:

fake delibility, effectively deny enumerability.

that's ok, because you can do it in the user land with Proxy. but you cannot make the global object itself becomes a Proxy.

If it's currently understood that it can set the global object's prototype, it probably can also delete, replace or reconfigure (even make non-configurable) the properties installed by SetDefaultGlobalBindings.

I agree. It is (if not, it must is) a formal requirement to implementations that all properties are configurable, even if they installed it in HostInitializeShadowRealm.