-
Notifications
You must be signed in to change notification settings - Fork 67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Normative: Coerce evaluate's argument to string #319
Conversation
It's important to highlight @caridy's PoV from #304 (comment) here:
|
@@ -292,12 +292,15 @@ <h1>Realm.prototype.evaluate ( _sourceText_ )</h1> | |||
<emu-alg> | |||
1. Let _O_ be *this* value. | |||
1. Perform ? ValidateRealmObject(_O_). | |||
1. If Type(_sourceText_) is not String, throw a *TypeError* exception. | |||
1. Let _sourceStr_ be ? ToString(_sourceText_). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not keep it throw? 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This came in as a suggestion from @gibson042's spec review (#304).
As I pointed out in the comment above, @caridy shared some agreement to this change in #304 (comment) and #318.
The high level overview for the change is to make the evaluate method consistent with importValue
and other general methods often seen in ECMAScript.
The main reason it throws today is to make it slightly consistent with eval
, but the consistency is not perfect. eval
will just return the argument if it's not a string:
const obj = { toString() { return "40 + 2"; } };
eval(obj) === obj; // and not 42
With the Realms API, we have the current outcome:
const obj = { toString() { return "40 + 2"; } };
const r = new Realm();
r.evaluate(obj); // throws TypeError
r.evaluate(new String('Math.PI')); // TypeError
With this PR, we could have some better usage of argument:
const obj = { toString() { return "40 + 2"; } };
const r = new Realm();
r.evaluate(obj); // 42
r.evaluate(new String('Math.PI')) === Math.PI; // true, considering Math.PI is not altered in both realms
We do have precedent for things like this today, such as the Function
constructor coercing args and body to string. The approach from this PR only makes evaluate
a bit more consistent with functionality of other methods and putting it slightly farther from eval
. Anyway, I believe the weird behavior from eval
on non string arguments is undesirable legacy, but part of web reality.
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd make sense to close this PR and leave the behavior as is. importValue
should be analogous to import()
(which coerces) and evaluate
should be analogous to eval
(which returns its input when passed not a string--the natural analogue is to throw, since that behavior is actually surprising).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to accidentally enables some attack surface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find validity in both sides, I don't have a strong preference and I can see a good reason either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That example falls into what I mentioned above, that there's similar behavior elsewhere, so I don't think it expands the "vulnerability" meaningfully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mikesamuel where is your proposal to use a non-string eval
argument for a trusted-type-like branding for approved evaluation in a CSP-no-eval context? If you could answer quickly, that'd be great because we're in the midst of a tc39 meeting where this may become relevant. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ljharb that's it. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand the benefit in coercing the source argument to a string, besides making it more similar to the Function
constructor. And given that the callable boundary prevents both access to the other Realm's Function.[[Construct]]
and passing non primitive arguments, it should be impossible to coerce to string and evaluate an object in another isolated realm using that route.
<emu-note type=editor> | ||
The argument _sourceText_ is coerced to a string value and this behavior differs from <emu-xref href="#sec-eval-x"></emu-xref>, which has a different behavior for non-String arguments. `eval(x)` returns `x` - with no evaluation - if `x` is not a String. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found @caridy's explanation satisfactory, and don't have strong feelings about throwing vs. coercing. But I do think a callout of the behavioral difference should remain visible in the rendered spec.
After some discussion with @caridy, we agreed to hold on and not move this PR forward for now. The reasons to keep the API as is have a good weight to also avoid such a noise before this TC39 meeting. I'm gonna hold on this PR and I'm pretty sure I can do a better recap to previous discussion and works such as those pointed out by @erights and @ljharb. Thanks everyone for the quick feedback! |
Ref #318