Proxy questions
On Jan 20, 2013, at 9:03 AM, David Bruant wrote:
Le 20/01/2013 16:49, Kevin Smith a écrit :
Got it. And one more (thanks for bearing with me) I forked the thread. Go crazy if you have other questions :-)
what affect does throwing an error have within a trap? You can think of it as an internal error, very much like Object.defineProperty throws a TypeError when the first argument isn't an object.
It's just an exception and propagates up the stack until handled. Very few (if any, I can't think of any without going and looking at the Proxy spec.) are handled in the Proxy implementation so the exception will propagate to the proxy.foo() caller.
var target = { foo: function() { this.bar; console.log("after bar"); },
when called via proxy.foo() this will be by proxy when foo is invoked. So so this.bar does a Proxy get trap that throws. If there was a try-catch around this.bar it would catch the exception.
bar: 0
};
var proxy = new Proxy(target, { get: function(obj, name) { if (name === "bar") throw new Error("boom"); return obj[name]; } });
Note that the full signature of the get trap is: function(obj, name, receiver). To work in all cases, the last line really needs to be: return Reflect.get(obj,name,receiver);
Does the stack unwind as with normal error handling? Does "after bar" get
logged? (FF18 answers yes to the first and no to the second question.)
I agree with FF18 here.
OK. Consider this:
class Purse {
private balance;
constructor() {
balance = 0;
Object.freeze(this);
}
getBalance() { return balance; }
somethingInocuous() {}
deposit(amount, srcPurse) {
private(srcPurse).balance -= amount;
this.somethingInocuous();
balance += amount;
}
}
A proxy for a Purse instance could throw on access to "somethingInocuous", leaving the high-integrity object in an inconsistent state. It would seem that a high-integrity object would need to assume that any access to a public property might throw, in case the object is being proxied.
Am I thinking about this correctly?
Before commenting on the general question, I'm confused about something in your code. How did a proxy get bound to "this" in your example?
Le 20/01/2013 20:11, Mark S. Miller a écrit :
Before commenting on the general question, I'm confused about something in your code. How did a proxy get bound to "this" in your example?
I would guess: var p = new Proxy(purseInstance, handler); Purse.prototype.deposit.call(p)
On Sun, Jan 20, 2013 at 10:49 AM, Kevin Smith <khs4473 at gmail.com> wrote:
A proxy for a Purse instance could throw on access to "somethingInocuous", leaving the high-integrity object in an inconsistent state. It would seem that a high-integrity object would need to assume that any access to a public property might throw, in case the object is being proxied.
Am I thinking about this correctly?
I think so. One way around that is that public methods only deal with private methods/properties (which may require aliasing public things to private equivalent?)
In case you run the untrusted code, you can provide a slightly less powerful Proxy constructor to prevent the untrusted code from doing this. Or a provide a slightly modified version of class methods which unwrap proxies or throw when seeing proxies.
Before commenting on the general question, I'm confused about something in your code. How did a proxy get bound to "this" in your example?
This is what I was thinking:
var blackHole = new Purse();
var blackHole = new Proxy(blackHolePurse, {
get: function(obj, name) {
if (name === "somethingInocuous") throw new Error("Up in
smoke!"); return obj[name]; } });
blackHole.deposit(1000, myPurse);
It's a pretty silly "attack", obviously : ) Apparently I'm not very good at being evil.
Let me correct a typo in that last one:
var blackHolePurse = new Purse();
var blackHole = new Proxy(blackHolePurse, {
get: function(obj, name) {
if (name === "somethingInocuous") throw new Error("Up in
smoke!"); return obj[name]; } });
try { blackHole.deposit(1000, myPurse); }
catch (x) { /* Oops - $1000 just disappeared from existence. */ }
Again - more foolish than evil. : )
I want to stress this again: proxies, for all operations they can intercept, can always decide to go into an infinite loop or throw. If they do throw, they can't "hide" their attack from your code. In that sense they don't violate the code's integrity. The "invariant enforcement" mechanism is based on the same assumptions: if the proxy detects that the handler behaves badly, throw to signal the problem.
The alternative to throwing would be for proxies to absorb any exceptions thrown by traps, but that would be worse (silent failures).
Cheers, Tom
2013/1/21 Kevin Smith <khs4473 at gmail.com>
I want to stress this again: proxies, for all operations they can intercept, can always decide to go into an infinite loop or throw. If they do throw, they can't "hide" their attack from your code. In that sense they don't violate the code's integrity. The "invariant enforcement" mechanism is based on the same assumptions: if the proxy detects that the handler behaves badly, throw to signal the problem.
The alternative to throwing would be for proxies to absorb any exceptions thrown by traps, but that would be worse (silent failures).
Sure.
Quoting you from the other thread:
Indeed. But in Javascript, methods shouldn't (in general) make any assumptions about their |this| values.
OK - but we can't have it both ways. We can't allow |this| to give us access to "private" data (regardless of the implementation) and also allow that |this| may be an untrusted proxy. If |this| grants access to private data, then it must be trustable. Anything else is inherently risky.
Kevin Smith wrote:
OK - but we can't have it both ways. We can't allow |this| to give us access to "private" data (regardless of the implementation) and also allow that |this| may be an untrusted proxy. If |this| grants access to private data, then it must be trustable. Anything else is inherently risky.
Who said otherwise?
First, as Tom noted, |this| binding in JS is dynamic unless you opt out.
Second, private symbols need not leak. We have had several designs to avoid leakage, and Tom' s advocating another that could work.
I know, you want to get rid of (defer, let's say) private symbols. But no one is trying to have it "both ways". Everyone wants to avoid private symbol leaks via proxies, in the face of dynamic |this| and other hazards of JS not related to |this|.
Le 20/01/2013 16:49, Kevin Smith a écrit :
I forked the thread. Go crazy if you have other questions :-)
You can think of it as an internal error, very much like Object.defineProperty throws a TypeError when the first argument isn't an object.
I agree with FF18 here. This case is relatively easy and the get trap is supported by Firefox implementation, but always aware that for now, some traps aren't implemented, some invariant-related checks aren't performed, other are overly performed. You can see the known issues in the dependency tree of the direct proxy bug: bugzilla.mozilla.org/showdependencytree.cgi?id=703537&hide_resolved=1