Running javascript dynamic code

I am making a small game, and for its part I want a very simple custom programming language. If the user enters the code, something like variable "helloWorld" = 5 , the "interpreter" will change the variable to var and drop the quotation marks like normal JavaScript.

How do I run this code? I read about eval() , but I also read it slowly and should not be used. I learned how to create programming languages ​​with lexers, parsers, and tokenizers, but I don’t want to create something detailed.

Any help with referral would be great.

+7
javascript dynamic interpreter
source share
4 answers

I assume that you do not need help with “How to write this code?”, But how to execute a user script.

About eval :

  • Is eval slow? Yes. How slow is slow? If the script runs in 10 ms compiled and 20 ms otherwise, is this a problem for you and your application?
  • Can user spoil eval? Yes! They can reassign functions, globals, etc. They may accidentally break the page.
  • This is dangerous? Yes! You may become vulnerable to XSS attacks. Do you have sensitive data? Do you have a server side for your application? If not, I think eval is fine.

Here's more info from various SO questions:

  • Use eval without XSS threat
  • JS eval security issue

The idea of ​​preventing global reassignment

Wrap the script in IIFE! Wrap the script as follows:

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

Javascript has a scope of functions, so this will protect the global space from being populated with user variables and functions. Users can still maliciously influence global variables as follows:

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

More on eval speed. 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, big-eval is a bit slower. You will probably do a big-eval by running all the lines of the user script at once. "Evil-eval" is much slower because the js engine runs 10,000,000 times! :)

+7
source share

It all depends on what you really want to do with your language. I would suggest that you really do not want the user to run arbitrary javascript that runs in the same global space in which your application lives.

So, depending on what you really want to do, you do not have to use eval() . For example, let's take your statement:

 variable "helloWorld" = 5 

Suppose you parse this and you parse it into an assertion object with four elements in an array:

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

OK, you can see from the first element of the array that the user is declaring a variable. Now you probably don't want user variables to fall into the global namespace (another reason not to use eval() . Therefore, instead you create an object for user variables:

 var userVars = {}; 

Now, when you process this above statement, you will see that this is the assignment of a user variable that you simply translate into:

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

You now have an object in a variable named userVars with a property named "helloWorld" with a value of 5 . None of the user variables in the global namespace can affect the operation of your own programs.

Now, obviously, if you want to get detailed expressions and a logical stream, then everything becomes more complicated, but this, I hope you will see how you can simply not use eval() in the global namespace.


Another "safe" way to execute arbitrary user javascript is to place it in an iframe hosted by a different domain than your main web page. You can send JS to your server and transfer it to another server (possibly in the same field), which then serves as an iframe, or you can use window.postMessage() to deliver JS text to another cooperating iframe and execute it.

Since the iframe is hosted in a different domain, it is isolated from your own web page and your own web application server. This is basically how jsFiddle works and allows the user to run arbitrary javascript while keeping the native application safe from many attacks.


Or, in the spirit of using window.postMesage() for an isolated iframe, you can even use eval() in this other iframe. If the JS user point needs to interact with your game, then you will need to figure out how to allow two iframes to talk to each other, but do it safely. This can be done using window.postMessage() , which is supported in IE8 or later.

The beauty of an isolated iframe is that it has its own isolated set of globals, and it cannot really conflict with your game, except through the whateve interface that you open through window.postMessage() .

+2
source share

your syntax may get some improvements, but the idea is ...

first you create a function:

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

The next step is to analyze the user input (which can lead to increased syntax). in your example, you can completely exclude quotes, then first divide the string by "=", and then left into empty space. you get 3 elements that represent (respectively): 1) the name of the function 2) the name of the variable 3) the variable value

then you call the function as follows:

 window[functionName](variableName, variableValie); 
0
source share

If you need the full expressiveness of JavaScript, then stick with raw JavaScript, as it is already familiar with a lot of people. You have two options for using raw JavaScript (without a bounding parser):

  • If you want the JavaScript that users put in your application to have full interactive privileges with your site, similar to how to allow privileged add-ons on your site (given the big risks: users may not know what they are doing or what else worse, they can be inserted from some malicious source, and full access will include allowing access to cookies that you may have used to store session information or passwords), then do eval() yourself.

  • If you want to get the full expressiveness of JavaScript, but do not want the user code to interfere with your application (and with the ability to place code on a separate site without access to your websites), you can use something like this for postMessage -based eval() Sandboxes: http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/#safely-sandboxing-eval

However, if you don’t need the full expressive power of JavaScript, and just want users to call your own API or something like that, I highly recommend considering using Blockly ( demo / tutorial ), as it is a graphical coding mechanism, It should be even more user-friendly and less error prone than having to manually create some custom syntax.

0
source share

All Articles