views:

169

answers:

6

I would really like to provide the user some scripting capabilities, while not giving it access to the more powerful features, like altering the DOM. That is, all input/output is tunneled thru a given interface. Like a kind of restricted javacsript.

Example: If the interface is checkanswer(func) this are allowed:

checkanswer( function (x,y)={
   return x+y;
}

but these are not allowed:
alert(1)
document.write("hello world")
eval("alert()")

EDIT: what I had in mind was a simple language that was implemented using javascript, something like http://stevehanov.ca/blog/index.php?id=92

A: 

Sounds like you need to process the user entered data and replace invalid mark-up based on a white list or black-list of allowed content.

Kieron
but that's not exactly what I would like, because first of all, such parsing could be quite easily circumvented by using encoding. What I had in mind was a simple language that was interpreted by javascript.
TiansHUo
+4  A: 

T.J. Crowder makes an excellent point about the "arms race." It's going to be very tough to build a watertight sandbox.

it's possible to override certain functions, though, quite easily.

Simple functions:

And according to this question, even overriding things like document.write is as simple as

document.write = function(str) {}

if that works in the browsers you need to support (I assume it works in all of them), that may be the best solution.

Alternative options:

  • Sandboxing the script into an IFrame on a different subdomain. It would be possible to manipulate its own DOM and emit alert()s and such, but the surrounding site would remain untouched. You may have to do this anyway, no matter which method(s) you choose

  • Parsing the user's code using a white list of allowed functions. Awfully complex to do as well, because there are so many notations and variations to take care of.

  • There are several methods to monitor the DOM for changes, and I'm pretty sure it's possible to build a mechanism that reverts any changes immediately, quite similar to Windows's DLL management. But it's going to be awfully complex to build and very resource-intensive.

Pekka
+5  A: 
T.J. Crowder
Plus of course every document object with an id/name gets dumped into `window` on IE... have fun filtering those out!
bobince
@bobince: *blech* Yeah, you'd probably have to combine the above with an iframe or something to handle those, so you "only" have to worry about the standard stuff, not the named/identified elements. (Updated answer with that, thanks, although it seems that actually letting users use Javascript *isn't* quite what he meant.)
T.J. Crowder
And then there's `new Function('...')()` to escape local scope. This isn't blockable as you can grab a `constructor` to get a `Function`. And `arguments.caller` to script up the call stack; `arguments` may not be overwritable. And fiddling with object prototypes later used by the caller. And returning an object with a sabotaged `toString` or properties. And so on. To summarise: aaaaargh. :-)
bobince
@bobince: Arrgh is right. :-) You can shadow `Function` and `arguments` on Chrome, Firefox, and IE. I'm not following your `constructor` argument (you can get *a* function, but how does that let you get the `Function` constructor?). But I'm with you that this is a *lot* of work and, as I said at the outset, an arms race.
T.J. Crowder
If you can get a function, you can get Function. So, to get around shadowing: `new ('hello'.constructor.constructor)('alert("hello from global")')`. Similarly, shadowing `arguments`, there's `(function(){ arguments.callee.caller.caller })()`... and so on. Arms race is right!
bobince
@bobince: Oh, too clever (the `.constructor.constructor` thing). (And for any doubters: I've tried it, and not surprisingly, he's absolutely right.)
T.J. Crowder
w00w what a geek-off :D didn't mean to offend anyone just its hard to understand what you guys are talking about
c0mrade
@c0mrade: summary: JavaScript is very flexible and also very ugly. :-)
bobince
@c0mrade: I tried to make it clear in the actual answer. The comments here are definitely a bit geek-off. :-) And (to me) very valuable, thanks Bob. Yes, Javascript is very powerful, very flexible, and with that comes some trickiness. (I don't agree with Bob's "ugly" characterization, but tomatoes tomahtoes...)
T.J. Crowder
Just for kicks now: `self` survives `Scoper` as a `window` reference. And naturally, `(function(){return this})()` -> `window`...
bobince
@bobince: LOL You're just having fun now, aren't you? :-) Re `self`, that's just another thing that needs shadowing. Whereas `(function(){return this;)()` is just yet another reason for my having added the statement that "...this **doesn't work**..." in the answer. ;-)
T.J. Crowder
A: 

You can do it the same way as Facebook did. They're preprocessing all the javascript sources, adding a prefix to all the names other than their own wrapper APIs'.

SK-logic
+2  A: 

Not really. JavaScript is an extremely dynamic language with many hidden or browser-specific features that can be used to break out of any kind of jail you can devise.

Don't try to take this on yourself. Consider using an existing ‘mini-JS-like-language’ project such as Caja.

bobince
if the sandbox needs to be really secure, this is probably the only worthwhile way.
Pekka
A: 

I got another way: use google gears WorkerPool api
See this
http://code.google.com/apis/gears/api_workerpool.html

A created worker does not have access to the DOM; objects like document and window exist only on the main page. This is a consequence of workers not sharing any execution state. However, workers do have access to all JavaScript built-in functions. Most Gears methods can also be used, through a global variable that is automatically defined: google.gears.factory. (One exception is the LocalServer file submitter, which requires the DOM.) For other functionality, created workers can ask the main page to carry out requests.

TiansHUo