tc39 / proposal-eventual-send

TC39 Eventual Send proposal
44 stars 6 forks source link

docs(eventual-send): update to clearer API inspired by #22 #26

Closed michaelfig closed 1 year ago

michaelfig commented 2 years ago

Closes: #22

Taking advice from @dead-claudia in #22, I'm proposing this revamp of the spec to improve its clarity and only introduce the Promise.delegated namespace for all the API additions.

michaelfig commented 2 years ago

We will wait on this change until @erights is available for further discussion.

dead-claudia commented 2 years ago

Note for attribution if/when you merge it: I've changed my @ from @isiahmeadows to @dead-claudia. Just thought I'd let you all know. 🙂

dead-claudia commented 2 years ago

@erights Not quite. It would be a fresh object if and only if the user provides a fresh object, stemming from the fact the method would treat it semantically as an opaque value. Concretely, it's really only changing the syntax for creation to reduce its surface area:

// Old
return new Promise.delegated((resolve, reject, resolveWithPresence) => {
    const presence = resolveWithPresence(getHandler())
    initializePresence(presence)
})

// This PR
const presence = Object.create(null)
const delegated = Promise.delegated.resolveWithPresence(presence, getHandler())
initializePresence(presence)
return delegated

If I'm understanding the proposal correctly, the core security component comes not from uniqueness or "freshness" of the presence, but from encapsulation of the handler.

Feel free to correct me if I'm wrong.

erights commented 2 years ago

Hi @dead-claudia the scenario I'm concerned with is where, say, Alice, creates a defensive object foo that she gives to Bob. Alice and Bob are mutually suspicious. Bob, with Alice's foo object, does

const delegated = Promise.delegated.resolveWithPresence(foo, getHandler());

Alice has also shared her foo object with Carol. Carol does, for example,

E(foo).bar();

Had Bob not done anything, this would be handled by foo.bar(). However, because of Bob's action, it is handled according to the handler Bob attached to it, subverting Alice's intentions.

The freshness guarantee prevents this. The presence cannot have been someone else's object.

dead-claudia commented 2 years ago

Hi @dead-claudia the scenario I'm concerned with is where, say, Alice, creates a defensive object foo that she gives to Bob. Alice and Bob are mutually suspicious. Bob, with Alice's foo object, does

const delegated = Promise.delegated.resolveWithPresence(foo, getHandler());

Alice has also shared her foo object with Carol. Carol does, for example,

E(foo).bar();

Had Bob not done anything, this would be handled by foo.bar(). However, because of Bob's action, it is handled according to the handler Bob attached to it, subverting Alice's intentions.

The freshness guarantee prevents this. The presence cannot have been someone else's object.

Already tried to addressed it, though apparently I left way too much implied: (emphasis added)

  • The presence is more or less just an early return value, and carries the same security concerns as a promise resolution value. If freshness and invisibility of the host prototype chain is a requirement for presences (say, it's code providing a service to multiple distrusted scripts), it'll also be a requirement for general return values, so there isn't any new untrodden ground here. It just means the same concern has to be addressed in two separate places. (It's not like this concern doesn't already exist for properties set to objects and functions - the attack surface area was already there.)

The intended implication here is that Alice, as she is suspicious of Bob, already would have to harden (read: replace prototype then optionally Object.freeze) any property she adds to her presence anyways. The only difference is she now has to harden the presence itself in full instead of just the assigned foo.bar on the presence.

Alice already has to know how to harden objects as she passes them to Bob and (presumably also) Carol to avoid exposing her own realm's prototypes, so it's only an extra step to harden the immediate object sent and not just the method.


I'm also looking at non-security-critical uses of this proposal, cases that simply leverage the eventual send functionality for incremental network communication but only have one trusted actor rather than several mutually suspicious actors.

dead-claudia commented 2 years ago

BTW, I'd recommend you all take a look at https://github.com/tc39/proposal-eventual-send/issues/31 and tell me what you think.

And of course, in an effort to keep the "send" part of the eventual send in, I'm pumping up the absolute historical lead balloon of a method call proxy trap, attempting to also use other use cases to further justify its inclusion.

michaelfig commented 1 year ago

I'm closing this PR because, while we didn't come to consensus on the freshness requirement for presences, I believe #31 is a much better foundation and bypasses the issue by not having a special category of presences at all.