Are Private name and Weak Map the same feature? and the Assoc API

David Bruant bruant.d at
Sat Dec 17 04:04:58 PST 2011

Le 17/12/2011 00:24, Sam Tobin-Hochstadt a écrit :
> On Fri, Dec 16, 2011 at 6:09 PM, David Bruant <bruant.d at> wrote:
>>> Requiring you to care in order to avoid leaking private names is incredibly error-prone.
>> So is requiring you to care in order to avoid leaking the public ->
>> private map. It's no more, no less dangerous or error-prone.
>> If I have to be careful about who I hand a reference to the public ->
>> private dictionary, how is it more dangerous or error-prone to be
>> careful about who you hand the private name to? It's the exact same work.
>> I think the difference lies in the habit we have in ES5 that "o[n]"
>> would not disclose a secret. Also, in ES5+private names "o[n]" does not
>> disclose a secret. My impression is that '.public' in an attempt to
>> preserve this property with proxies.
>> I propose to take the other approach which is to accept that "o[n]" may
>> disclose 'n' if 'o' is a proxy. I agree this is a big change in how we
>> think objects.
> I used to think this way about private names and proxies, and argued
> for this position. However, I was wrong and it's a bad idea.  Here's
> why:
> You might want to implement an object with a private field and a
> public method like this:
> let o = (function () { let n = new Name(); return { [n]: 0; get:
> function() { return this[n]; }}})();
I propose to accept that "o[n]" may disclose 'n' if 'o' is a proxy. So,
assuming it is accepted, then "this[n]" may be a form of disclosure
depending on the value of |this|.
I propose to accept it. (One good news is that mostly static analysis
can assist us in finding where we may disclose 'n' in this scenario).

> But, if we don't censor private names in proxies, this abstraction leaks!
I agree, but this is not inherent to private names, it is inherent to
how you wrote your example. In a world where "o[n]" may disclose 'n',
exposing a method with the instruction "this[n]" is a dangerous thing to
write, indeed.
It is as dangerous as writing "". No more no less.

> Consider:
> let p = new Proxy(...);
In the case "get:function(){}", consider:;

the 'get' trap of the proxy is called with 'foo' as name, the proxy
decides to create a function. This function gets called with 'n' as
argument. 'n' is disclosed.
> Now the name is leaked to the proxy.
In my case too and it is as unacceptable, but in no way it is more or
less dangerous.

> And there's no way you can fix
> this, without resorting to some other method of hiding things like
> closures.
I disagree, there is.
In the case where you want the proxy ("worst case", here "proxy" means
"untrusted third party") to have access to the private name, you need
the proxy to have access to a public->private map. And this is the way
you have hidden your private name. Your private name is not hidden
thanks to the language, but thanks to the fact that you do not provide
the public->private map.
In the end, your security does not rely on the private name not being
disclosed. It relies on your public -> private map not being disclosed.

The problem remains the same, but shifted somewhere else.

> The problem you raise, that ocap-style security relies on not leaking
> data *ever*, is, I think, a real one.  But it's one that this solution
> for proxies *mitigates*, because almost all the time,
I'm sorry, but regarding features that are not deployed yet, I cannot
agree with a sentence with "almost all the time".

> you don't ever
> want to care about keeping track of which names have which public
> version, as in the example I gave.
I have been written proxy abstractions where passing private names would
be handy:
1) makeReadOnly(o)
Creates a proxy that wraps o to make a read-only view of it. Passing
private names transparently would be handy to make the abstraction work
with private names. But of course, public->private dictionaries work too.

2) makeRecorded(o)
Creates a proxy that record every change made to 'o'. Recording which
change refers to which private name is part of it too. The goal is to be
able to replay the different states o went through.

3) makeBranch(o)
Creates an object that takes on it all changes 'o' should go through.
Changes related to private names are part of it too.

In all this cases, if I create a proxy and someone else has a private
name, everything should go fine with a simple dictionaries.
But I'm sure that if we start having different actors, some with
references to o, some with only the proxy, some with both, some with
private names applied to either o or the proxy, we will end up with
things that are not possible to do or ridiculously complicated when it
comes to sharing authority (at the risk of disclosing the map to the
wrong parties).

> You only need this is you want to
> allow proxies to support your private name, which isn't the case for
> every abstraction. But it is the case that without the censoring,
> proxies break almost every abstraction built using private names.
Proxies and private names are not deployed in any platform, so maybe
without censoring, proxies break abstraction, but as of December 17th
2011, this is a backward compatibility issue only with undeployed and
untested code. I think there is time to get people used to the idea that
"o[n]" and "this[n]" may be an act of disclosure.

Deciding to not censor does not break anything except the abstractions
people who are aware of proxies and private names have /thought/ of.

If each time I create an abstraction using proxies, I need a
public->private map, it is likely that I (and it's obviously not about
"me", but anyone writing proxies) won't use private names with proxies.
I may just use WeakMaps.
What I need is a (object, some unforgeable object) mapping inside the
proxy. If the cost of using private names is private+public+map, I can
achieve what I want more efficiently with weakmaps and sharing a single
(unforgeable) weakmap reference among trusted parties.


More information about the es-discuss mailing list