Private symbols vs property attributes
Le 07/02/2013 12:58, Andreas Rossberg a écrit :
We intend to have both "unique" and "private" symbols. The only difference between the two is that the latter are filtered from certain reflective operations.
I have come to think that this distinction is backwards. It is attributing something to symbols that actually is an attribute of properties. Symbols are just symbols.
This would force proxies to have the third whitelist argument regardless of what's decided on the proxy_symbol_decoupled strawman strawman:proxy_symbol_decoupled This is because some symbols (@@iterate, @@create, libraries extending built-ins with symbols-as-collsion-free-property-names etc.) need to pass proxies transparently, while what is currently private symbols shouldn't pass by default.
I don't have an opinion yet on whether it's a good or bad thing, but I just wanted to point it out.
We should not piggyback them with something that is not actually related to their own semantics as such, but only their forwarding in specific client contexts.
Let's put the distinction where it belongs. There is no systematic difference between privateness and non-enumerability, so they should be handled analogously. I hence propose that we add a new attribute to property descriptors, say, 'private'. Any property with this attribute set to true is filtered by the relevant reflective operations. That is, it is simply a stronger form of the non-enumerable attribute. (For consistency a logically inverted attribute like 'reflectable' might be preferable, but that's secondary.)
The only drawback I see with this approach is that we have to pick a default. In particular, an assignment o[s] = v where s is a symbol that does not exist yet on o can only have one meaning, either consistently introducing a private property or a non-private one. There are valid arguments for either choice, but I think making the choice is doable.
Current string semantics begs for private:false (since string-as-property-name are always reflected) But unique symbols, used as collision-free extension of built-ins could take the hit. I guess overriding @@iterate and @@create is also rare enough that using Object.defineProperty for that is also acceptable, leaving the PrivateSymbol semantics being the default. But I'm arguing for private:true by default here... Valid arguments for either choice indeed :-)
On 7 February 2013 13:23, David Bruant <bruant.d at gmail.com> wrote:
Le 07/02/2013 12:58, Andreas Rossberg a écrit :
We intend to have both "unique" and "private" symbols. The only difference between the two is that the latter are filtered from certain reflective operations.
I have come to think that this distinction is backwards. It is attributing something to symbols that actually is an attribute of properties. Symbols are just symbols.
This would force proxies to have the third whitelist argument regardless of what's decided on the proxy_symbol_decoupled strawman strawman:proxy_symbol_decoupled This is because some symbols (@@iterate, @@create, libraries extending built-ins with symbols-as-collsion-free-property-names etc.) need to pass proxies transparently, while what is currently private symbols shouldn't pass by default.
The interplay with proxies is less clear, I admit. My gut feeling is that this decomposition actually frames it in the right way, and forces us to stay more honest, but I may be wrong. :)
The basic idea would be that the the privacy attribute found on the target (if any), determines which operations/names are to be treated privately. For new properties, the provided attribute decides. This semantics could be amended by a white list. AFAICS, that mainly leaves the corner case of attempting to delete a property that does not actually exist on the target (because there is no privacy attribute available in that case). Should that trap? A somewhat simpler corner case is reconfiguring the privacy attribute from true to false or vice versa. I don't have a good answer for those cases off-hand, but expect them to be solvable.
On Feb 7, 2013, at 3:58 AM, Andreas Rossberg wrote:
We intend to have both "unique" and "private" symbols. The only difference between the two is that the latter are filtered from certain reflective operations.
I have come to think that this distinction is backwards. It is attributing something to symbols that actually is an attribute of properties. Symbols are just symbols. We should not piggyback them with something that is not actually related to their own semantics as such, but only their forwarding in specific client contexts.
Let's put the distinction where it belongs. There is no systematic difference between privateness and non-enumerability, so they should be handled analogously. I hence propose that we add a new attribute to property descriptors, say, 'private'. Any property with this attribute set to true is filtered by the relevant reflective operations. That is, it is simply a stronger form of the non-enumerable attribute. (For consistency a logically inverted attribute like 'reflectable' might be preferable, but that's secondary.)
The only drawback I see with this approach is that we have to pick a default. In particular, an assignment o[s] = v where s is a symbol that does not exist yet on o can only have one meaning, either consistently introducing a private property or a non-private one. There are valid arguments for either choice, but I think making the choice is doable. A property of the other form would have to be introduced via Object.defineProperty, until we get more syntax support. (Note that this only affects installing a new property, not later assignments.)
Note that the enumerable attribute really only affects for-in enumeration (and Object.keys), neither of which enumerates symbols anyway. That, means that the enumerable attribute really has has no current meaning for symbol keyed properties. That means we could probably reinterpret the enumerable attribute as a "private" attribute for such symbol keyed properties.
For now, I'll reserve comment on whether or not any of this is a good idea and related issues such as proxy impacts.
On 8 February 2013 00:41, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
Note that the enumerable attribute really only affects for-in enumeration (and Object.keys), neither of which enumerates symbols anyway. That, means that the enumerable attribute really has has no current meaning for symbol keyed properties. That means we could probably reinterpret the enumerable attribute as a "private" attribute for such symbol keyed properties.
I wasn't aware of that, and don't understand it. Why wouldn't I want unique (as opposed to private) symbols to show up? Is that special casing for backwards-compatibility?
Allen Wirfs-Brock wrote:
Note that the enumerable attribute really only affects for-in enumeration (and Object.keys), neither of which enumerates symbols anyway. That, means that the enumerable attribute really has has no current meaning for symbol keyed properties. That means we could probably reinterpret the enumerable attribute as a "private" attribute for such symbol keyed properties.
Groovy.
But the private-as-attribute idea still seems to require an access control check, which makes it less secure from an OCap perspective and experience, compared to symbols as capabilities.
Wishing for Mark to weigh in here!
I do not understand what is being proposed. When I try to imagine a proposal starting from what has been said, I have not been able to imagine something that works. But that's not a criticism. What is this alternate privacy idea?
Le 10/02/2013 08:07, Brendan Eich a écrit :
Allen Wirfs-Brock wrote:
Note that the enumerable attribute really only affects for-in enumeration (and Object.keys), neither of which enumerates symbols anyway. That, means that the enumerable attribute really has has no current meaning for symbol keyed properties. That means we could probably reinterpret the enumerable attribute as a "private" attribute for such symbol keyed properties.
Groovy.
But the private-as-attribute idea still seems to require an access control check, which makes it less secure from an OCap perspective and experience, compared to symbols as capabilities.
I'm not sure I understand your concern. Under Andreas proposal, a symbol would remain an unforgeable token. The only thing that changes is how the symbol is shared. In the proposal being discussed setting private:false in the property descriptor would be a way to share a symbol. That's less direct than handing access to the symbol only, but that's a very explicit way anyway, so I don't see a problem from an ocap perspective. That said, to force the author to be explicit about sharing symbols indirectly through reflection, private:true should probably be the default when doing "obj[symb] = 34".
Le 10/02/2013 17:16, Mark S. Miller a écrit :
I do not understand what is being proposed. When I try to imagine a proposal starting from what has been said, I have not been able to imagine something that works. But that's not a criticism. What is this alternate privacy idea?
My understanding is:
-
there is only one kind of symbol
-
whether the symbol is reflected by Object.getOwnPropertyNames and the likes is controlled by a 'private' attribute in property descriptors.
// unique constructor, no boolean since there is only one kind of symbol var s = new Symbol(); var o = {}, o2 = {};
Object.defineProperty(o, s, {value: 12, private: true}); assert(Object.getOwnPropertyNames(o).length === 0) assert(o[s] === 12); o[s] = 31; assert(o[s] === 31);
Object.defineProperty(o2, s, {value: 7, private: false}); assert(Object.getOwnPropertyNames(o)[0] === s); assert(o2[s] === 7); o2[s] = 13; assert(o2[s] === 13);
Pending question: var o3 = {}; o3[s] = 62; Object.getOwnPropertyDescriptor(o3, s).private // true or false?
Since private:false implies symbol sharing through Object.getOwnPropertyDescriptor, I think private:true should be favored to force people to be explicit (see my reply to Brendan)
The main difference with the current proposal is that privacy isn't an inherent characteristic of the symbol, but related to how it's been configured on the different objects it's been used on.
Was the above one of the things you imagined? If yes, why doesn't it work?
How does this interact with Proxies[1]? I know the answer probably starts with "whitelist", but let's spell it out in this context, and test it against the 8 membrane transparency cases previously discussed. If there are good answers for all of these, and if we can reuse enumerable: for this purpose as Brendan suggests, then I'm guardedly positive. I do not want to introduce a new attribute.
[1] For now, I agree that the proxies we should examine are direct proxies.
David Bruant wrote:
- whether the symbol is reflected by Object.getOwnPropertyNames
That's already spec'ed in ES5 and it never returns non-string names, so no symbol (name) proposal has ever extended it. At least as far as I know!
I have come to think that this distinction is backwards. It is attributing something to symbols that actually is an attribute of properties. Symbols are just symbols. We should not piggyback them with something that is not actually related to their own semantics as such, but only their forwarding in specific client contexts.
Agreed - thanks for sharing this perspective. I think that regardless of what happens with respect to private state, we must only have one kind of symbol.
On 10 February 2013 19:40, Mark Miller <erights at gmail.com> wrote:
How does this interact with Proxies[1]? I know the answer probably starts with "whitelist", but let's spell it out in this context, and test it against the 8 membrane transparency cases previously discussed. If there are good answers for all of these, and if we can reuse enumerable: for this purpose as Brendan suggests, then I'm guardedly positive. I do not want to introduce a new attribute.
I must have missed the discussion of the "8 membrane transparency cases". Do you have a pointer?
I'm fine with reusing 'enumerable'. Finally, a useful function for that attribute. :)
Le 12/02/2013 16:06, Andreas Rossberg a écrit :
On 10 February 2013 19:40, Mark Miller <erights at gmail.com> wrote:
How does this interact with Proxies[1]? I know the answer probably starts with "whitelist", but let's spell it out in this context, and test it against the 8 membrane transparency cases previously discussed. If there are good answers for all of these, and if we can reuse enumerable: for this purpose as Brendan suggests, then I'm guardedly positive. I do not want to introduce a new attribute. I must have missed the discussion of the "8 membrane transparency cases". Do you have a pointer?
I think it is esdiscuss/2013-January/028481
Andreas Rossberg wrote:
I'm fine with reusing 'enumerable'. Finally, a useful function for that attribute.:)
You and everyone else!
Yes. During a break at TC39 I sketched a very nice diagram of this to, IIRC, Rick. I stupidly did not take a photo. When I find the time I'll try to reproduce. In the meantime, hopefully the idea is clear enough.
2013/2/10 Mark Miller <erights at gmail.com>
How does this interact with Proxies[1]? I know the answer probably starts with "whitelist", but let's spell it out in this context, and test it against the 8 membrane transparency cases previously discussed.
When thinking about symbol "leaks", we must consider two cases: a) leaking a symbol by having it show up in reflective query methods on ordinary objects b) leaking a symbol by inadvertently applying a symbol-keyed operation on a proxy
Andreas' proposal of having a symbol's enumerability depend on a property attribute makes a lot of sense and deals with problem a). OTOH, it does not address leaks of type b). In order to prevent those, proxies currently use the whitelist.
If we lose the a-priori distinction between unique and private symbols and introduce only 1 type of symbol, then proxies must treat all symbols like they currently treat private symbols.
The annoying thing about that is that well-known symbols like @@create and @@iterator must be explicitly added to a proxy's whitelist in order for the proxy to intercept them, but at least it's doable.
W.r.t. membranes, AFAICT this proposal changes nothing re. the interaction between private symbols and proxies. Membranes would still need the "unknownPrivateSymbol" trap to stop unknown private symbol access from piercing the membrane.
On Wed, Feb 13, 2013 at 11:17 AM, Tom Van Cutsem <tomvc.be at gmail.com> wrote:
2013/2/10 Mark Miller <erights at gmail.com>
How does this interact with Proxies[1]? I know the answer probably starts with "whitelist", but let's spell it out in this context, and test it against the 8 membrane transparency cases previously discussed.
When thinking about symbol "leaks", we must consider two cases: a) leaking a symbol by having it show up in reflective query methods on ordinary objects b) leaking a symbol by inadvertently applying a symbol-keyed operation on a proxy
Andreas' proposal of having a symbol's enumerability depend on a property attribute makes a lot of sense and deals with problem a). OTOH, it does not address leaks of type b). In order to prevent those, proxies currently use the whitelist.
If we lose the a-priori distinction between unique and private symbols and introduce only 1 type of symbol, then proxies must treat all symbols like they currently treat private symbols.
The annoying thing about that is that well-known symbols like @@create and @@iterator must be explicitly added to a proxy's whitelist in order for the proxy to intercept them, but at least it's doable.
W.r.t. membranes, AFAICT this proposal changes nothing re. the interaction between private symbols and proxies. Membranes would still need the "unknownPrivateSymbol" trap to stop unknown private symbol access from piercing the membrane.
AFAICT, that trap wouldn't provide transparency for the membrane crossing cases. Is there anything about this new proposal that could improve on that?
Le 13/02/2013 21:56, Mark S. Miller a écrit :
On Wed, Feb 13, 2013 at 11:17 AM, Tom Van Cutsem <tomvc.be at gmail.com <mailto:tomvc.be at gmail.com>> wrote:
2013/2/10 Mark Miller <erights at gmail.com <mailto:erights at gmail.com>> How does this interact with Proxies[1]? I know the answer probably starts with "whitelist", but let's spell it out in this context, and test it against the 8 membrane transparency cases previously discussed. When thinking about symbol "leaks", we must consider two cases: a) leaking a symbol by having it show up in reflective query methods on ordinary objects b) leaking a symbol by inadvertently applying a symbol-keyed operation on a proxy Andreas' proposal of having a symbol's enumerability depend on a property attribute makes a lot of sense and deals with problem a). OTOH, it does not address leaks of type b). In order to prevent those, proxies currently use the whitelist. If we lose the a-priori distinction between unique and private symbols and introduce only 1 type of symbol, then proxies must treat all symbols like they currently treat private symbols. The annoying thing about that is that well-known symbols like @@create and @@iterator must be explicitly added to a proxy's whitelist in order for the proxy to intercept them, but at least it's doable. W.r.t. membranes, AFAICT this proposal changes nothing re. the interaction between private symbols and proxies. Membranes would still need the "unknownPrivateSymbol" trap to stop unknown private symbol access from piercing the membrane.
AFAICT, that trap wouldn't provide transparency for the membrane crossing cases. Is there anything about this new proposal that could improve on that?
The trap in itself no, but it's possible to keep track of all exchanged symbols and add them to the whitelists as they are observed before being shared. It all relies on the fact that for 2 parties to exchange symbols, they have to share it through a "public" communication first (get trap, etc.). At some point, I thought it had a runtime cost, but Tom proved me wrong [1]. It seems realistic to consider that all proxies of the same membrane can all share the same set instance as a whitelist making the space cost as small as it could be.
David
2013/2/13 David Bruant <bruant.d at gmail.com>
The trap in itself no, but it's possible to keep track of all exchanged symbols and add them to the whitelists as they are observed before being shared. It all relies on the fact that for 2 parties to exchange symbols, they have to share it through a "public" communication first (get trap, etc.).
At some point, I thought it had a runtime cost, but Tom proved me wrong [1].
It seems realistic to consider that all proxies of the same membrane can all share the same set instance as a whitelist making the space cost as small as it could be.
This doesn't solve Mark's use case: he wants to be able to share symbols freely among trust boundaries without those symbols necessarily flowing through a membrane. At that point, there's no way for the proxy to intercept the symbol and add it to its whitelist, and transparency is broken.
As I said before, I think Andreas' proposal is agnostic to the interaction between proxies and symbols.
I think this proposal gets it right from privacy side of things, but the interaction with proxies is not good. Proxies would have to treat any symbol as potentially naming a private property, and therefore all symbols used by the object would have to be added to the proxy's whitelist when wrapping the object. The usability here is horrible. If you added just one symbol to the internal interface of an object, the whitelists at all trusted proxy-creating sites would also have to be updated.
In fact, the same usability critique applies to the current private symbols proposal as well.
Basically, "get" and "getPrivate" are two distinct operations from an MOP perspective. Private symbols are a kludge used to overload "get" based on the type of symbol used as input.
Some points:
- ES6 is already chock-full of new features.
- In ES6, there are already multiple ways to "secure" object data.
- Private properties interact poorly with our current MOP, as evidenced by proxy issues.
Let's not throw this private property kludge into ES6.
(I realize that using the word "kludge" is a bit baiting, but I intentionally want to cast that light on it.)
Kevin Smith wrote:
If you added just one symbol to the internal interface of an object, the whitelists at all trusted proxy-creating sites would also have to be updated.
Not arguing with your larger point, but this is why the whitelist is a live object, that is, the proxy code consults it by reference instead of making a copy. Tom has shown that for membrane use-cases a shared mutable WeakSet singleton.
We intend to have both "unique" and "private" symbols. The only difference between the two is that the latter are filtered from certain reflective operations.
I have come to think that this distinction is backwards. It is attributing something to symbols that actually is an attribute of properties. Symbols are just symbols. We should not piggyback them with something that is not actually related to their own semantics as such, but only their forwarding in specific client contexts.
Let's put the distinction where it belongs. There is no systematic difference between privateness and non-enumerability, so they should be handled analogously. I hence propose that we add a new attribute to property descriptors, say, 'private'. Any property with this attribute set to true is filtered by the relevant reflective operations. That is, it is simply a stronger form of the non-enumerable attribute. (For consistency a logically inverted attribute like 'reflectable' might be preferable, but that's secondary.)
The only drawback I see with this approach is that we have to pick a default. In particular, an assignment o[s] = v where s is a symbol that does not exist yet on o can only have one meaning, either consistently introducing a private property or a non-private one. There are valid arguments for either choice, but I think making the choice is doable. A property of the other form would have to be introduced via Object.defineProperty, until we get more syntax support. (Note that this only affects installing a new property, not later assignments.)