Killing `Promise.fulfill`

Mark S. Miller erights at google.com
Thu Aug 22 08:09:13 PDT 2013


On Thu, Aug 22, 2013 at 8:04 AM, Mark S. Miller <erights at google.com> wrote:

> 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.
>

Btw, the problem this code illustrates is a problem Brendan pointed out to
me at the last TC39 meeting.


>
>
>
>>
>> > 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
>



-- 
    Cheers,
    --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130822/0998e443/attachment.html>


More information about the es-discuss mailing list