Running dynamic Javascript code

点点圈 提交于 2021-02-07 12:23:05

问题


I'm making a small game and for part of it I want a really simple custom programming language. If a user enters code, something like variable "helloWorld" = 5, the "interpreter" would change variable to var and drop the quotes to be normal JavaScript.

How should I run that code? I've read about eval(), but I've also read it's slow and shouldn't be used. I've looked into creation of programming languages with lexers, parsers, and tokenizers, but I'm not looking to create something that in-depth.

Any help with direction would be great.


回答1:


I assume you don't need help with "How to write that code?", but how to execute the user script.

About eval:

  1. Is eval slow? Yes. How slow is slow? If a script runs in 10ms compiled and 20ms otherwise, is it a problem to you and your application?
  2. Could the user mess up with eval? Yes! They could reassign functions, globals, etc. They could accidentally break the page.
  3. Is it dangerous? Yes! You could become vulnerable to XSS attacks. Do you have any sensitive data? Do you have a server side to your application? If not, I think eval is ok.

Here is more information from different SO questions:

  • Use eval without threat of XSS
  • JS eval security issue

An idea about preventing global reassignment

Wrap the script in an IIFE! Wrap the script like this:

(function(){
// user script goes here.  This will cause it to be in it's own scope!
})();

Javascript has function scope so this will protect the global space from getting filled with user variables and functions. Users could still maliciously affect global variables like this:

(function(){Array.isArray = function() { return 2;};})()
Array.isArray([]);
// returns 2    

More about the speed of eval. A real example:

#!/bin/env node
// Be careful running this.  You don't want to melt your cpu.  Try 100,000 first.
console.time("no-eval"); 
for (var i = 0; i < 10000000; i++) { Math.sqrt(i); }  
console.timeEnd("no-eval");
console.time("big-eval"); 
eval("for (var i = 0; i < 10000000; i++) { Math.sqrt(i); }");
console.timeEnd("big-eval");
console.time("evil-eval"); 
for (var i = 0; i < 10000000; i++) { eval("Math.sqrt(i);"); }  
console.timeEnd("evil-eval");

Output:

no-eval: 272ms
big-eval: 294ms
evil-eval: 1945ms

As you can see the 'big-eval' is a little slower. You will probably do the big-eval, running all lines of the user script at once. The 'evil-eval' is much slower because the js engine is running the eval 10,000,000 times! :)




回答2:


This all depends upon what you really want to do with your language. I would guess that you really don't want the user to run arbitrary javascript that runs in the same global space in which your app lives.

So, depending upon what you really want to do, you don't necessarily need to use eval(). For example, lets take your statement:

variable "helloWorld" = 5

Let's suppose you parse that and you parse it into a statement object with four items in an array:

var statement = ["variable", "helloWorld", "=", 5];

OK, you can see from the first item in the array that the user is declaring a variable. Now, you probably don't want the user's variables to go in the global namespace (another reason not to use eval(). So instead, you create an object for the user's variables:

var userVars = {};

Now, when you process that above statement, you will see that it's an assignment to a user variable which you just translate into:

userVars[statement[1]] = statement[3];

Now, you have an object in a variable named userVars with a property named "helloWorld" with a value of 5. None of the user's variables are in the global namespace or could ever affect your own programs operation.

Now, obviously if you want to get into detailed expressions and logic flow, then things get more difficult than this, but I hope you see how you may not just want to use eval() into the global namespace.


Another "safe" way to execute arbitrary user javascript is to put it in an iframe that is hosted by a different domain than your main web page. You can either send the JS to your server and have it pass it to a different server (probably on the same box) that then serves the iframe or you can use window.postMessage() to deliver the JS text to the other cooperating iframe and execute it there.

Because the iframe is hosted on a different domain, it is isolated from your own web page and your own web's app server. This is basically how jsFiddle works and allows the user to run arbitrary javascript while keeping their own app safe from many attacks.


Or, in the vein of using window.postMesage() to an isolated iframe, you could even use eval() in that other iframe. If the point of the user's JS is to interact with your game, then you'd have to figure out how to allow the two iframes to talk to one another, but do so safely. It can be done with window.postMessage() which is supported in IE8 or newer.

The beauty of the isolated iframe is that is has it's own isolated set of globals and can't really mess with your game at all except through whateve interface you expose via window.postMessage().




回答3:


your syntax might get some improvements but the idea is like this...

first, you create function:

function variable(name, value){
window[name] = value;
}

next step is to parse user input (that's where syntax improvements could come into play). in your example, you could strip off quotations completely, then divide string first by '=' and then left part by blank space. you get 3 elements that represents (respectively): 1) function name 2) variable name 3) variable value

then, you call function like this:

window[functionName](variableName, variableValie);



回答4:


If you need the full expressivity of JavaScript, then stick with raw JavaScript as it's already familiar to more people. You have two possibilities in using raw JavaScript (without a confining parser):

  1. If you want to allow the JavaScript that users put into your application to be able to have full interactive privileges with your site, in a manner akin to allowing privileged add-ons to your site (bearing in mind the great risks: that users may not know what they are doing or, worse, may have pasted from some malicious source, and full access will include allowing access to cookies which you may have used to store session information or passwords), then go ahead and subject yourself to eval().

  2. If you want the full expressivity of JavaScript but don't want user code interfering with your application (and with the freedom to host the code on a separate site without access to your site's cookies), then you can use something like this for postMessage-based eval() sandboxing: http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/#safely-sandboxing-eval

If you don't need the full expressive power of JavaScript, however, and just wish users to call your own custom API or something, I would strongly recommend considering using Blockly (demo/tutorial), as it, being a graphical coding mechanism, should be even more user-friendly and less error-prone than having to create some custom syntax manually.



来源:https://stackoverflow.com/questions/22340975/running-dynamic-javascript-code

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!