Javascript Safe eval method using Narcissus

(filed under programming)

In my new web project (I will talk about it later... ;-) I need a way to evaluate javascript that is contributed by third party users of the site.

But javascript is fundamentally unsafe, because the browser objects are globally available in javascript. Once you give your users access to add javascripts to your site, they can alter your site completely!
They can steal cookies and crosspost it to their own domain, pull up unexpected html, ... the possibilities for evil hackers are endless.

So, I started to look around for some solutions:

These approaches did not work out. Adsafe was too restricted. It turns out that in order to get safe javascript, you really need to throw a lot of things out. Even the *this* keyword:

  function TryThis(value)
    if (this.alert)
      this.alert("Hi I can take control over your window!");

  var o=new TryThis(100); // if you use the function this way
                          // 'this' is just a harmless object.

  // but if you leave out the new keyword..
  // then 'this' points to the browsers window object! 

So AdSafe strips javascript until nothing that I wanted is left in the language. Caja requires java, which I do not want to use. Ruby2js was not very well documented, and seemed more a command line utility than an API usable in the web server. I didn't try really hard to get this running, though, because I prefer that the users can enter javascript and don't have to learn another language for the client side.

Porting Narcissus

But then I thought, if javascript itself is so leaky, wouldn't it be much safer if I just could start a separate javascript interpreter in the browser. And luckily, there is a javascript interpreter written in javascript. It's called, for obvious reasons, Narcissus. So I downloaded it, and discovered that it runs in no single browser at all. This is because the author of Narcissus is also the author of spidermonkey, and he used spidermonkey specific code, that isn't even available in firefox yet.

It took some hours hard debugging (especially to make it work on IE too), but I've got a version running that works on IE, firefox, google chrome. It is a quick and dirty port, not thoroughly tested. Probably deep down it is not 100% compatible with all the language features of javascript.
But for 99.9% compatibility, it will work great. Another disclaimer: at present it uses global objects AND it pollutes your Object.prototype which isn't very nice if you use a lot of javascript libraries.

Running javascript in javascript

It is amazing to see that you can actually run javascript in javascript. Haven't run any performance tests yet, but for short scripts performance looks fine. It is not possible to access browser objects from the narcissus scripts (let me know if you disagree with that). You can expose browser functionality by wrapping it into functions. (Don't wrap it into objects because then they can be readed by the narcissus scripts)
Here's how you use it:

  // to wrap the browser alert function, assign it to the
  // narcissus global object. 
  global.alert=function(s) { 
     window.alert("Narcissus alert:"+s);
  // evaluate: script, name, line number.
  // name and line number are for error reporting,
  // but this seems to be broken. 
  evaluate("alert('hello world');",'eval.js',1); 

As you will see, the alert that pops up says Narcissus alert:hello world. So the alert function is no longer the browser alert function, and there is no way you can access any browser object (like window, document, top, history, XMLHTTPRequest...) from the Narcissus script.
I have made a sandbox, where you can try it out.

Blog menu