#Safety of Roll.safeEval
1 messages · Page 1 of 1 (latest)
Not sure if this really makes it safe, there might be more escape hatches. But it's better than the current state, from my understanding.
Safety of Roll.safeEval
Also, is there a reason (other than the use of with for this piece of code), that foundry is not using strict mode in general? That seems like a... risky thing to me...
It's still possible to escape such a sandbox:
Roll.safeEval('(() => {ceil.constructor("console.log(\\"foo\\")")(); return 0;})()');
You could maybe use a Proxy to make a more bulletproof sandbox, though I'm not sure if it's possible to make a truly bulletproof one. What would be the motivation though? Protecting users from pasting strange formulae into AE fields?
Also, is there a reason (other than the use of with for this piece of code), that foundry is not using strict mode in general? That seems like a... risky thing to me...
What do you mean by 'using strict mode in general'? Do you have an example of a place it's not being used where it should be?
Yes, that’s the motivation. Because right now, this effectively allows users to perform arbitrary code execution in all other connected users‘ clients. Not an issue if you trust your players, but not everybody does (fully), and it’s just a bit dangerous
It’s already using a Proxy (Roll.MATH_PROXY). I don’t think the snippet you wrote would work for that reason, but I’ll try it myself
Not a specific location, but just in general. I was under the impression that "use strict"; is kinda the default in this day and age, but it’s not used by foundry at all, from what I understand.
Well ES6 modules and class bodies are all strict mode anyway, which is 99% of foundry's code
A script which contains mostly classes, yes
Well, I won’t argue about it, I was just surprised by it.
JavaScript's strict mode is a way to opt in to a restricted variant of JavaScript, thereby implicitly opting-out of "sloppy mode". Strict mode isn't just a subset: it intentionally has different semantics from normal code. Browsers not supporting strict mode will run strict mode code with different behavior from browsers that do, so don't rely o...
I think it's not possible to fully sandbox eval. Since we have a bespoke parser with Roll though, we could maybe use that
yeah, I guess I'll need to either do that, or do some manual parsing... :/
new Roll('1 + 1').evaluate({async: false}).total will handle a fair bit
The main issue is that async: false is going away
it even works when there is nothing in the context at all, since you can do
Roll.safeEval('(() => {(function foo() {}).constructor("console.log(\\"foo\\")")(); return 0;})()');
yeah, I'll have to look into that... Probably not going to actually instantiate a roll, since there can be structure in my values that wouldn't be accepted by the parsing logic. But I can take inspiration from it...
But is there any sense in having safeEval at all, then?
Unless .roll() can remain synchronous, safeEval will remain necessary.
It's used by Roll.replaceFormulaData() -> Roll.safeEval() combo in places where async code is impossible.
I think I now have a proper solution for this. It might be a bit overkill, but I'm pretty happy with it. The idea is pretty simple: I wrote a relatively simple lexer for a relevant subset of JavaScript. Then I define a set of safe allowed identifiers (which for example does not include constructor, etc.). And before actually evaluating an expression, I run the input through the lexer, and check if there are any identifier tokens that would not be allowed. And only if that is not the case, is the expression actually evaluated.
This still gives a lot of flexibility, since you can easily evaluate any expression that just uses the allowed identifiers (which basically is just the properties of Math, true, false, null, and undefined, and a few safe operators), that conforms to the subset of JS that is supported (which exlcudes arrow functions, for example).
but it should be safe
Why not just copy _evaluateSync?
I might have to do that myself.
Edit: Nope, it relies on async:false, too.
Edit2: Needs more overrides, one for RollTerm, too, which unfortunately requires monkeypatch.