ajvincent / es-membrane

An ECMAScript implementation of a Membrane, allowing users to dynamically hide, override, or extend objects in JavaScript with controlled effects on the original objects.
ISC License
109 stars 13 forks source link

General membrane question: why shadow target? #181

Closed zenparsing closed 6 years ago

zenparsing commented 6 years ago

Hi @ajvincent!

Every membrane implementation in JS that I've taken a look at makes use of a "shadow target" instead of directly using the "dry" object as the proxy target.

Thanks!

cc @erights

erights commented 6 years ago

cc @tvcutsem

Hi @zenparsing , I wish we had given the concepts names that were more different than "shadow target" and "real target", as those names suggest a similarity that always causes confusion. Further, the specification language for the Proxy mechanism refers to the shadow target as "target", suggesting more similarity to the real target than the shadow target. Note that the specified mechanism has no knowledge of the real target, and indeed, there may not actually be a real target. The proxy's behavior can well emulate an object that has no independent existence.

Staying within the membrane usecase, focusing on a particular proxy, I'll refer to that proxy as the "dry proxy", its shadow target as "dry shadow", and the real target as the "wet target". (Sorry if that is opposite the dry/wet direction in your question.)

The purpose of the dry shadow is only for bookkeeping, to keep track of what stability guarantees the dry proxy's behavior has committed it to, so that the proxy's subsequent behavior cannot violate those stability guarantees. The best example is if a dry user asks the dry proxy

Object.getOwnPropertyDescriptor(p, 'foo')

and the handler wants the proxy to claim in response that its foo property is a non-configurable non-writable data property with value 8. To allow the proxy to make this claim, the proxy mechanism must somehow keep track of this commitment, to prevent the proxy from later making a claim that is inconsistent with having made the above claim earlier. For example, a later getOwnPropertyDescriptor cannot claim that the foo property has value 9.

The mechanism forces the handler to represent stability claims on the dry shadow in order to make this claim. It must install a non-configurable non-writable data property with value 8. Any claim by the dry proxy that this property has another value is then rejected because it is inconsistent with the dry shadow.

In the membrane use case, say that wet target X has a non-configurable non-writable data property whose bar property is wet target Y. The transparent membrane behavior would be for dry proxy XP to claim to have a non-configurable non-writable data property whose bar property is dry proxy YP. In order to make this claim, the bookkeeping mechanism forced XP's handler to first create, on dry shadow XS, a non-configurable non-writable data property whose bar property whose bar property is dry proxy YP. That's why the dry proxy's shadow is dry. The data values it commits to exist to record what data values its proxy is now committed to.

A transparent membrane cannot be implemented without the shadow technique. A non-transparent membrane that never made any stability guarantees could ignore the shadow, since the mechanism would never see any stability claims it needs to track.

erights commented 6 years ago

See Trustworthy Proxies: Virtualizing Objects with Invariants by @tvcutsem and me.

zenparsing commented 6 years ago

Thanks @erights, this is exactly what I was looking for.

In the last meeting, you mentioned the desire to have some kind of mechanism that can be used to prove that modifications to the language do not result in any security regressions. Perhaps this means a formalization of security invariants, or perhaps a set of tests? Do you have any further thoughts on the topic? What do you think would be a good direction to explore here?

ajvincent commented 6 years ago

@zenparsing: @erights and I expect to draft a strawman proposal for TC39 which defines a place for ECMAScript integration tests, and how they're organized internally. That said, we're both very busy, so we'd welcome assistance and/or volunteer efforts to help us drive this forward.

I think the original question in this issue has been answered, so I'm closing the issue.