Private symbols auto-unwrapping proxies (was: Security Demands Simplicity (was: Private Slots))

Allen Wirfs-Brock allen at
Tue Jan 22 10:25:17 PST 2013

On Jan 22, 2013, at 9:06 AM, Brandon Benvie wrote:

> Kevin Smith pointed out something I hadn't though about before but is obvious in retrospect. The hazard that proxies bring to the language in general: proxies make it possible to break the target object by inconsistently handling forwarding during the operation of methods.
>     class Counter {
>       constructor(){
>         this.value = 0;
>       }
>       increment(){
>         this.state = "working";
>         this.value++;
>         this.state = "idle";
>         return this.value;
>       }
>     }
>     const counter = new Counter;
>     const counterfeit = new Proxy(counter, {
>       get(target, key, receiver){
>         if (key === 'value') {
>           throw 'break target';
>         }
>         return Reflect.get(target, key, receiver);
>       }
>     });
> While this may not be common, and the example is contrived, it seems like it may be less uncommon when there's a trap that exists who's sole purpose is to throw or not.
> It is my opinion that the primary use case for private symbols is for properties that proxies expressly shouldn't be given a chance, in any manner, to corrupt or modify. They are likely used for sensitive internal state that will only be accessed by methods or friend classes created in service of the target.
> A membrane becomes less valuable if breaking the target is an easily accomplished accidental side effect. This is already visible in practice today when you attempt to use WeakMaps to create private state for objects and they are proxied, since the private state will be keyed on `this` in the constructor which won't match `this` in methods invoked on the proxy.

Right, more concretely:

let fooRegistry = new WeakMapl
class Foo {
   constructor() {
         fooRegistry.set(this,true);  //should really be in a @@create method to prevent counterfeiting
   op() {
       if (!fooRegisty.has (this)) throw Error("Foo called on non-Foo object");

let f = new Foo;
let pf = new Proxy(f, {});

f.op();   //no problem
pf.op();   //throws

This problem has nothing to do with private Symbols and is the same problem Tom and I talked about in 

As that thread discussed, the root cause is that the lookup that retrieved a method is a separate MOP level operation from the call of the method and there currently is no mechanism to coordinate the this value between those MOP operations.

On way to fix this would be to introduce a new MOP operation [[CallProperty]] that combines [[Get]]/[[Call]].  I know Brendan has opposed this when it has come up in the past (bad E4X experiences) but I'm not talking about any new user level semantics (other than making Proxy work reasonably).  The ordinary object definition of [[CallProperty]] would have exactly the same semantics as occurs for the [[Get]]/[[Call]] sequence that evaluates for expr.key()

Another way to address this issue avoids introducing a new MOP level operation. Instead we would introduce a new kind of Reference value that is used for the result of Proxy [[Get]] operations. The [[Call]] op would know about these special Proxy References and adjust the this value accordingly before invoking the function.  Something similar to that is currently in the spec. to support super based property accesses. 

I think, [[CallProperty]]  actually has less hair, but either one could be used to make default Proxy behavior be consistent forwarding to the target with the target value always substituted for this position proxy values.  We would get rid of the confusing and error prone combination of forwarding and delegation that is now the default for Proxy.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the es-discuss mailing list