Mark S. Miller (2013-08-22T15:04:05.000Z)
On Wed, Aug 21, 2013 at 10:22 PM, Tab Atkins Jr. <jackalmage at gmail.com>wrote:

> On Wed, Aug 21, 2013 at 5:55 PM, Mark S. Miller <erights at google.com>
> wrote:
> > On Wed, Aug 21, 2013 at 5:33 PM, Tab Atkins Jr. <jackalmage at gmail.com>
> > wrote:
> >>
> >> On Wed, Aug 21, 2013 at 5:12 PM, Mark S. Miller <erights at google.com>
> >> wrote:
> >>> On Wed, Aug 21, 2013 at 3:44 PM, Tab Atkins Jr. <jackalmage at gmail.com>
> >>> wrote:
> >>>> But I like just Promise(), sans "new".
> >>>
> >>> Good. I like it too, and Allen's latest draft class semantics enables
> >>> the
> >>> definition of classes that can do this reliably. But is there any way
> to
> >>> reliably polyfill this in ES5? It's not clear. For the std promise API,
> >>> given the urgency with which people want to use it *now*, I think this
> >>> polyfillability is important.
> >>
> >> Yes, why wouldn't it be?  The test for promise-ness can be done today,
> >> without special browser privilege, and distinguishing between
> >> `Promise(foo)` and `new Promise(foo)` is a trivial matter of testing
> >> the type of `this` inside the function.  (If it's instanceof Promise,
> >> you were called with new.  Otherwise, you weren't.  This isn't 100%,
> >> as people can fool you with Promise.call(), but that's not something
> >> you need to actually worry about, I think.)
> >
> >
> > This is an example of what I am worried about. Another is
> >
> >     Object.create(p, {value: Promise}).Promise(....)
> >
> > where p is a promise.
>
> I'm not quite sure what this is supposed to do, because it's invalid
> Object.create.  (Or rather, it creates a property named "value" with
> default descriptors, which I'm sure isn't intended.)
>

You're right, my code is wrong. What I meant is:


      Object.create(p, {Promise: {value: Promise}}).Promise(....)

In other words, the Promise constructor might get supplied as its "this" an
object that passes "p instanceof Promise" for innocent reasons other that
"new" or .call. The question is, what does the Promise constructor test in
order to determine whether it should use its coercion behavior or
constructor behavior. If the test were "p instanceof Promise", then the
above call, which was clearly intending to invoke its coercion behavior,
would accidentally invoke its constructor behavior instead.



>
> > Perhaps it would help if, when we start to think "people can fool you
> > with..." it would help to substitute "an attacker can fool you with...".
>
> What exactly is the attack scenario being envisioned here, though?
>

The promise constructor must create initialize and return trustworthy
promises, and it must not mark as trustworthy a promise that isn't. The
promise coercer must return trustworthy promises. If given an object
returned by the promise constructor or the promise coercer, the promise
coercer must return that object.



> Okay, you can call a function and supply your own `this` value.  And?
> You can always do this, before or after construction.


If we weren't trying to put both coercing and constructing behavior into
one function, switched on some unreliable test, then there wouldn't be a
new problem. Postponing subclassing till ES6 when we have the needed
support, the Promise constructor could just create and return a new
trustworthy promise (just as the first branch of the "solution" I present
below does). Promise.as would simply be the conditional we discussed
earlier (and corresponds to the second branch of the "solution" below). But
if we put these two behaviors in one function, what test do we use to
choose between them?



>  I don't think
> this lets you fool anything, because you, the attacker, have to run
> your own code to make it happen.


The attacker does run their own code.


>  You can't somehow trick the defender
> into creating a tricky not-quite-authentic Promise, unless you've
> tricked them into using eval() or something.  If the attacker is
> getting to run code of its choosing, you've already lost.
>

Uh, Tab, attackers always run code of their choosing. But they don't run
that code with authority of their choosing. In an ocap system like SES,
"eval" provides no authorities by default. See

<http://theory.stanford.edu/~ataly/Papers/sp11.pdf>
<http://erights.org/talks/thesis/markm-thesis.pdf> Part II,
<http://research.google.com/pubs/pub40673.html> section 2.3,
and the "video" and "slides" links at <
http://mobicrant-talks.eventbrite.com/>



>
> Am I missing some obvious attack?
>

Obvious perhaps only if you've been thinking about this kind of thing.
Please have a look at those links.


In any case, postponing subclassing till ES6 when we have the needed
support, I think I know how to "solve" the problem. It is a bit weird.

    var Promise = (function(){
        "use strict"; // of course

        var brand = new WeakMap();

        // only ever called with "new"
        function HiddenPromiseConstructor(callback) {
            // initialize "this", which it can assume starts fresh and
trustworthily uninitialized
            brand.set(this, true);
        }

        function Promise(arg) {
            if (Object.getPrototypeOf(this) === Promise.prototype &&
!(brand.has(this))) {
                // assume likely called with "new", but do not trust "this"
                return new HiddenPromiseConstructor(arg)
            } else {
                // assume called for coercion behavior. Ignore this
                if (brand.has(arg)) {
                    return arg;
                } else {
                    return Promise.of(arg);
                }
            }
        }
        HiddenPromiseConstructor.prototype = Promise.prototype;

        // initialize Promise.prototype
        // initialize Promise statics
        return Promise
    })();



>
> ~TJ
>



-- 
    Cheers,
    --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130822/ac9b5790/attachment-0001.html>
domenic at domenicdenicola.com (2013-08-29T19:23:38.374Z)
On Wed, Aug 21, 2013 at 10:22 PM, Tab Atkins Jr. <jackalmage at gmail.com>wrote:

> I'm not quite sure what this is supposed to do, because it's invalid
> Object.create.  (Or rather, it creates a property named "value" with
> default descriptors, which I'm sure isn't intended.)

You're right, my code is wrong. What I meant is:

      Object.create(p, {Promise: {value: Promise}}).Promise(....)

In other words, the Promise constructor might get supplied as its "this" an
object that passes "p instanceof Promise" for innocent reasons other that
"new" or .call. The question is, what does the Promise constructor test in
order to determine whether it should use its coercion behavior or
constructor behavior. If the test were "p instanceof Promise", then the
above call, which was clearly intending to invoke its coercion behavior,
would accidentally invoke its constructor behavior instead.



> > Perhaps it would help if, when we start to think "people can fool you
> > with..." it would help to substitute "an attacker can fool you with...".
>
> What exactly is the attack scenario being envisioned here, though?
>

The promise constructor must create initialize and return trustworthy
promises, and it must not mark as trustworthy a promise that isn't. The
promise coercer must return trustworthy promises. If given an object
returned by the promise constructor or the promise coercer, the promise
coercer must return that object.



> Okay, you can call a function and supply your own `this` value.  And?
> You can always do this, before or after construction.


If we weren't trying to put both coercing and constructing behavior into
one function, switched on some unreliable test, then there wouldn't be a
new problem. Postponing subclassing till ES6 when we have the needed
support, the Promise constructor could just create and return a new
trustworthy promise (just as the first branch of the "solution" I present
below does). Promise.as would simply be the conditional we discussed
earlier (and corresponds to the second branch of the "solution" below). But
if we put these two behaviors in one function, what test do we use to
choose between them?



>  I don't think
> this lets you fool anything, because you, the attacker, have to run
> your own code to make it happen.


The attacker does run their own code.


>  You can't somehow trick the defender
> into creating a tricky not-quite-authentic Promise, unless you've
> tricked them into using eval() or something.  If the attacker is
> getting to run code of its choosing, you've already lost.

Uh, Tab, attackers always run code of their choosing. But they don't run
that code with authority of their choosing. In an ocap system like SES,
"eval" provides no authorities by default. See http://theory.stanford.edu/~ataly/Papers/sp11.pdf http://erights.org/talks/thesis/markm-thesis.pdf Part II, http://research.google.com/pubs/pub40673.html section 2.3, and the "video" and "slides" links at http://mobicrant-talks.eventbrite.com/

> Am I missing some obvious attack?
>

Obvious perhaps only if you've been thinking about this kind of thing.
Please have a look at those links.


In any case, postponing subclassing till ES6 when we have the needed
support, I think I know how to "solve" the problem. It is a bit weird.

```js
var Promise = (function(){
    "use strict"; // of course

    var brand = new WeakMap();

    // only ever called with "new"
    function HiddenPromiseConstructor(callback) {
        // initialize "this", which it can assume starts fresh and trustworthily uninitialized
        brand.set(this, true);
    }

    function Promise(arg) {
        if (Object.getPrototypeOf(this) === Promise.prototype && !(brand.has(this))) {
            // assume likely called with "new", but do not trust "this"
            return new HiddenPromiseConstructor(arg)
        } else {
            // assume called for coercion behavior. Ignore this
            if (brand.has(arg)) {
                return arg;
            } else {
                return Promise.of(arg);
            }
        }
    }
    HiddenPromiseConstructor.prototype = Promise.prototype;

    // initialize Promise.prototype
    // initialize Promise statics
    return Promise
})();
```