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

Brandon Benvie brandon at brandonbenvie.com
Tue Jan 22 09:06:34 PST 2013


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.



On Tuesday, January 22, 2013, Tom Van Cutsem wrote:

> 2013/1/22 Allen Wirfs-Brock <allen at wirfs-brock.com <javascript:_e({},
> 'cvml', 'allen at wirfs-brock.com');>>
>
>> We can probably fix the built-ins with some ad hoc language about them
>> automatically resolving proxies to the target as the this value. Or perhaps
>> we could expand the internal MOP api  to include a resolve proxy to target
>> operation.
>>
>> Using private symbols for all of these cases, including the built-ins
>> also seems like an alternative that may work.
>>
>
> Let me try to summarize:
>
> The proposal: private symbol access auto-unwraps proxies.
>
> In code:
>
> var s = new PrivateSymbol();
> var t = {};
> var p = Proxy(t, {...});
> t[s] = "foo"
> p[s] // doesn't trap, returns "foo"
> p[s] = "bar" // doesn't trap, sets t[s] = "bar"
>
> Pro:
> - would solve the issue of wrapping class instances with private state
> stored via private symbols
> - would solve the issue of how to proxy built-ins, like Date, if they are
> specified to use private symbols to access internal state
> - would get rid of the unknownPrivateSymbol trap in Proxies
> - could maybe even get rid of the private symbol whitelist in the Proxy
> constructor, which would making proxies entirely oblivious to private names
>
> Remaining issue: private symbols can pierce membranes.
>
> This issue is resolved if:
> - (base case) there are no built-in private symbols in a standard JS
> environment (i.e. all the built-in symbols are unique)
> - (inductive case) a membrane takes care to detect and wrap any private
> symbols that cross the membrane, and keeps a 1-to-1 mapping to maintain the
> identity of the symbols across both sides of the membrane.
>
> It's worth noting that revocable proxies (those created using
> Proxy.revocable) would also stop forwarding private symbol access when
> revoked.
>
> Cheers,
> Tom
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130122/09b7c131/attachment-0001.html>


More information about the es-discuss mailing list