My ECMAScript 7 wishlist

Mark S. Miller erights at google.com
Fri Jun 6 08:38:13 PDT 2014


On Fri, Jun 6, 2014 at 7:37 AM, David Bruant <bruant.d at gmail.com> wrote:

>  Le 06/06/2014 15:57, Mark S. Miller a écrit :
>
> By contrast, a Map's state is more like the private instance variable
> state of a closure or a post-ES6 class.
>
> The capabilities to arbitrarily modify Maps (set/delete on all keys, with
> any values) will be expected by any ES6-compliant code to be globally
> available, so a Map's state cannot reasonably be considered private.
> This differs from the state of a closure where its access is strictly
> moderated by the public API giving access to it and to the fact that this
> API is not provided globally (unlike Map.prototype).
>

That's a good point, but doesn't really address what I'm trying to say. The
map's state is manipulable through its API surface, rather than being the
properties which are its API surface. In the abstract perhaps this is a
subtle distinction, but it is the distinction Object.freeze is built on.

Consider if you wrote in JavaScript a Map abstraction of your own that held
the data structures of your map implementation in private instance
variables. Object.freeze would clearly not touch your map implementation.
For builtins, internal properties serve the role that private instance
variables will in class instances.

This does bring up an interesting issue though. Even if you wanted your map
to respond to Object.freeze by making this internal implementation be no
longer mutable, you can't, unless the Map is a proxy, because no other JS
object can sense when it is being frozen. More on this in another email
coming soon.

  Object.freeze of a Map should not alter the mutability of this state for
> the same reason it does not alter the state captured by a closure or a
> future class instance.
>
> I'd argue the Map state is very much like regular objects (for which you
> can't deny [[Set]], [[Delete]], etc.), not closure's state.
>
> In an ES6 world, denying access to the global Map.prototype.* would break
> legitimate code, so that's not really an option confiners like Caja could
> provide.
>

Why would we want to deny access to Map.prototype.* ? If there were a Map
among the primordials, for example, if Map.prototype itself were a Map,
then, in combination with the primordial existence of Map.prototype.set,
we'd have a global communications channel. (Indeed, SES repairs
Date.prototype when Data.prototype is a Date, and likewise for WeakMap <
https://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/repairES5.js#2806>
<https://bugzilla.mozilla.org/show_bug.cgi?id=861219>.)


>
>> or should an Object.makeImmutable be introduced? (it would be freeze +
>> make all internal [[*Data]] objects immutable)
>>
>
>  We do need something like that. But it's a bit tricky. A client of an
> object should not be able to attack it by preemptively deep-freezing it
> against its wishes.
>
> I don't see the difference with shallow-freezing?
> It's currently not possible to defend against shallow-freezing (it will be
> possible via wrapping in a proxy).
>

Therefore a defensible API surface in JS cannot contain as part of its
contract that it presents data properties that remain writable. Except as
you point out, for proxies which refuse to be frozen. This restriction on
the possibilities for defensible API surfaces is an unfortunate consequence
of the unpleasant choices we had for securing ES5, as you point out below.

>
>      This can be achieved with Proxy right, or is that too cumbersome?
>>
>>  Code-readability-wise, wrapping in a proxy is as cumbersome as a call to
>> Object.preventUndeclaredGet I guess.
>>
>> This sort of concerns are only development-time concerns and I believe
>> the runtime shouldn't be bothered with these (I'm aware it already is in
>> various web). For instance, the TypeScript compiler is capable today of
>> catching this error. Given that we have free, cross-platform and fairly
>> easy to use tools, do we need assistance from the runtime?
>>
>
>  Yes. Object.freeze is a runtime production protection mechanism, because
> attacks that are only prevented during development don't matter very much
> ;).
>
> Just to clarify, I agree that Object.freeze was necessary in ES5 (have we
> had proxies, it might have been harder to justify?), because there was no
> good alternative to protect an object against the parties it was shared
> with.
>

Exactly. But even with proxies, proxies are way way too heavy to be the
only form of defensible object. ES6 classes also give us a place to stand
to start to talk about the difference between provider and client.
Hopefully ES7 will support defensible classes that make defensible
instances. But because of the historical path these decisions took, I
expect that this class-based defensiveness will only be sugar for a pattern
of freezing anyway, so it won't make a fundamental difference.



> But the concern Nicholas raises doesn't seem to have this property.
> Reading a property that doesn't exist doesn't carry a security risk, does
> it? Object.preventUndeclaredGet doesn't really protect against anything
> like ES5 methods did.
>

That's true, but misses the point I was trying to make. For normal ES
objects, it is already part of their API contract with their clients that
the clients can do feature testing to detect the presence or absence of a
method. The most common way to do such feature testing is to get the
property and see if it is falsy. (Variations include, testing for
undefined, testing for undefined or null, and testing if its typeof is
"function".) It's fine if the provider of an abstraction does not wish to
support this pattern. But it is not ok for one client of an object which
does support it to prevent that object's other clients from successfully
using feature detection.

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


More information about the es-discuss mailing list