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?

4
  • 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. 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. Commented Jul 26, 2013 at 3: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. Commented Jul 26, 2013 at 3: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 = .... Commented Jul 26, 2013 at 5: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: developer.appcelerator.com/blog/2010/12/… Commented Jul 27, 2013 at 20:43

2 Answers 2

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)

Sign up to request clarification or add additional context in comments.

Comments

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.

4 Comments

What if code calls another function and that function is not in strict mode? Or executes eval itself?
Aside from some sickening regular expressions I don't know how to prevent that.
See my answer, I provide two ways to prevent that as well as links to repl implementations.
Curious, could a var window = {}; be placed at the beginning of the evaluated function?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.