Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running dynamic Javascript code

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.

like image 227
user3408714 Avatar asked Mar 12 '14 02:03

user3408714


People also ask

What is JavaScript dynamic code?

JavaScript has a function to create code dynamically and the code may contain variables, objects and functions. This code is stored in a string that is given as an argument to the built-in function eval. Eval may be used with JSON. This format is an alternative to XML that is easier in its definition and its use.

How to load script JavaScript?

For loading a script file dynamically using JavaScript, the basic steps are: Create the script element. Set the src attribute on the script element to point to the file we want to load. Add the script element to the DOM.


2 Answers

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! :)

like image 77
Jess Avatar answered Oct 02 '22 14:10

Jess


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().

like image 25
jfriend00 Avatar answered Oct 05 '22 14:10

jfriend00