4

I am writing a simple REPL (Read, Evaluate, Print, Loop) implementation in JavaScript. I am able to isolate code and calling context like so:

var sandbox = {
  // Allow the input code to use predefined helper functions
  // without the preceding use of the this keyword.
  helper_fn: function() { alert("foo"); }
};

var func = new Function("with (this) { " + user_inputed_code + " }");
func.call(sandbox);

Now it closes the user_inputed_code so that this referers to sandbox and if the inputted code accesses or mutates this it is affecting sandbox.

However, I noticed that if the imputed code were to accidentally forget to preface a variable assignment with the keyword var that the global namespace get polluted.

Is there anyway to prevent this? If so how (maybe a regexp?)? Is there a better way to approach this?

Sebas
  • 21,192
  • 9
  • 55
  • 109
Sukima
  • 9,965
  • 3
  • 46
  • 60
  • Can you *eval* the code in a separate frame or window that has a separate *global* object? Incidentally, in ECAMScript, "context" is used in relation to [*execution context*](http://www.ecma-international.org/ecma-262/5.1/#sec-10.3). A particular execution context's *this* parameter is only one small part of "context" that refers to all the parameters and variables, scope chain, etc. – RobG Jul 26 '13 at 03:18
  • I plan to use this in a Titanium project. So frames are not available. However freeze might work. Will have to see if the runtime supports that. – Sukima Jul 26 '13 at 03:30
  • Can you run the code in strict mode? That way assignment to undeclared variables will throw an error. You can't use *with* though. And users can still create globals using `window.foo = ...`. – RobG Jul 26 '13 at 05:10
  • @Sukima I just noticed that you said you're using Titanium, it lets you create a new separate execution context with the createWindow command. Your application can have multiple execution contexts. New execution contexts are typically created by opening a new window that points to an external URL in its url property: `Ti.UI.createWindow({url:'yourscriptfile.js'}).open();` You can message pass between contexts. See this guide: http://developer.appcelerator.com/blog/2010/12/titanium-guides-project-js-environment.html – Benjamin Gruenbaum Jul 27 '13 at 20:43

2 Answers2

2

I'm going to provide two completely different approaches from what others discussed here. They are both drastic, and are useful when you want to relatively isolate your environment.

  • The easiest technique that works in all browsers is to probably create an iframe, and append script tags to it. (Note, a really overzealous iframe can still get past that if they're in the same domain, at least in older browsers). I discuss that in this question.
  • Use web Workers, which have an isolated environment by default and have no access to the global object of the main execution thread. I discuss that in this question.

More specifically, if you're building a REPL take a look at this answer where we discuss and I explain how to eval code in the same scope but outside the global scope, using the first approach of iframes.

(I assumed a browser, in node you can simple use the vm module and select the context in runInContext)

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
1

Turns out there is a way with "use strict" and without Object.freeze. You have to manually replace the global namespace with your own sandbox object:

var sandbox, module, func, output;
// Empty object or with defined methods / properties
// you want to expose as globals.
sandbox = {};
// A reference to an object you WANT to provide safe
// access to. In this example it's just an empty object.
module = {};
// A better version of eval:
func = new Function("module", "with(this){return(function(module,global,window){\"use strict\";return eval(\"" + code + "\");})(module,this,this)}");
output = func.call(sandbox, module);

This code allows global and window to refer to a sandboxed object instead of the global name space. It masquerades the variables global and window as the sandbox object and the use of "use strict" will cause it to throw an exception if the input missed the use of var. It also wraps the function in a with statement to make the methods and properties defined in the sandbox object to work as if they were preceded by this.. To see an implementation example (with test specs) check out this gist.

Thank you all for your ideas. Hope this answer helps others.

Sukima
  • 9,965
  • 3
  • 46
  • 60