Isolate execution of JavaScript
Asked Answered
I

5

22

One of the limitation of JS that bugs me the most is the poor ability to isolate code's execution.

I want to be able to control the context in which the code is executed, Something that achieve a similar effect to what Script.createContext & Script.runInContext in node.js does (node is using binding to the V8 engine, so i can't emulate their implementation).

Here is the some reason why I want to isolate code execution:

  1. Isolate the code from the global namespace (the window object and the also the DOM) , but I however need to be able reference function call on objects exposed in the context which must be executed synchronous which makes it almost impossible using a WebWorker for isolation.
  2. By isolate the execution of code it would possible also be able to deallocate its definitions when no longer needed (memory management).

I know one may achieve partly isolated execution by loading script into a iframe, this approach is however very heavy and uses a lot memory for a second instance of the DOM which isn't needed for what I'm trying to do.

I need to share constructor definition and also definitions of object which are shared between the isolated containers/contexts which both must run on the main UI thread. Mainly i want to use these isolated containers to host plugins/modules (mini-applications) which each presents and dynamically updates a viewport by calling drawing commands on their own Context2D object.

If these containers are not running on the main UI thread it wold be painfully hard to proxy calls such as ctx.measureText() and ctx.drawImage() would be all useless as image objects can't be created in a Worker.

Does someone know of future specification that would make this possible?

Are there any current (hidden) browser-side APIs that could be used to achieve this?

Would it be better utilize a virtual machine like Goggle's Dart VM and also re-implement my current codebase? My current codebase is slightly above 20 000 lines of code.

Would it be better to re-implement the framework in *

Ignescent answered 29/4, 2012 at 22:39 Comment(4)
Do you have an existing problem that must be fixed like this? IIABDFI.Tabitha
Agree with the comment, but downvote is a bit unnecessary.Saransarangi
@minitech I do actually have a problem with the lack of sandoxes (which is a better word for what i described above) in the browser runtime, due to i'm trying to achieve a plugin architecture for my client-side platform, which is written in JavaScript and utilizes the browser runtime for such. The platform is generic and it's main purpose is host the environment for dynamically loading and control plugins/modules/mini-application which provides the platform with additional features.Ignescent
@Ignescent did you find a solution for your problem? I need to make something similar (third party plugins running in main, financial (yeah!) app) for cross platform use. Our top candidate now is ReactNative but the isolation feels like nightmarish impossible. (oh, 4 years later. oops.)Deiform
A
3

You can isolate your code from the global namespace with a simple self executing function object:

(function() {
   // all your code goes here
   // nobody outside of your code can reach your top level variables here
   // your top level variables are not on the window object

   // this is a protected, but top level variable
   var x = 3;

   // if you want anything to be global, you can assign it to the window object.
   window.myGlobal = {};

   function myTopLevelFunction(x,y,z) {
       // code here
   }

})();

If you want to have multiple ones of these execution contexts and be able to share between them, then you will have to rendezvous via one publicly known location, either a truly global variable or a property on a known DOM object or something like that. It is relatively common to declare one global namespace object and use properties off that for any access to things you're sharing among modules. I know it isn't completely perfect, but it works. Here's an example of the rendevous using a single global namespace object:

// module AAA
(function() {
   // module AAA code goes here

   // set up global namespace object and whatever references we want to be global
   window.myModuleTop = window.myModuleTop || {};
   myModuleTop.AAA = {};
   myModuleTop.AAA.myFuncA = function() {};

})();


// module BBB
(function() {
   // module BBB code goes here

   // set up global namespace object and whatever references we want to be global
   window.myModuleTop = window.myModuleTop || {};
   myModuleTop.BBB = {};
   myModuleTop.BBB.myFuncB = function() {};

})();
Ajaajaccio answered 29/4, 2012 at 23:6 Comment(3)
I am not sure if this is the type of isolation OP is asking. (But I am not sure I understand him too)Saransarangi
@Matt, I'm not trying to keep the OP's code from getting back to window. That's always easy. I'm trying to keep other people out of the OP's code and keep the OP's code isolated from other people's code.Ajaajaccio
@Ajaajaccio yeah I think there's some confusion on what the OP is looking forExcursion
E
3

The closest library I've seen for this is Caja.

Basically, in non-strict javascript code, there are many ways to get access to the global object (window in browsers), making true isolation a very hard problem. Caja does some iframing trickery to patch this, but to be honest I'm not exactly sure how it works.

Excursion answered 29/4, 2012 at 23:7 Comment(5)
If you force strict mode, by doing something like evaling a script, can this be much easier?Differ
@BT use strict has certainly improved the situation, but there are still so many things you'd have to patch. It's far from a safe guarantee. For example, invoking setTimeout or similar things can be done on window implicitly, even if you override it: (function(window) { 'use strict'; setTimeout(function() { console.log(this); }); }({})); <- this will print the global window despite the lexical overrideExcursion
But by the same token, you could lexically override setTimeout just like you're doing for window for a more full result.Differ
@BT yep, but the point is there are so many ways to get global, no amount of lexical overrides should be considered secure.Excursion
Gotcha, I'm curious if you know where I could look to find all these unscrupulous ways to get to the global object.Differ
D
3

Couldn't you use a closure like other answers mentioned and then use a shadow dom to ensure that the user can't get to the rest of the dom? Something like this:

var containerNode = someDomNode
var root = containerNode.createShadowRoot()
;(function(root){
  var window = null, document = null, history = null,
      screen = null, navigator = null, location = null

  // isolated code goes here

})(root)

Caveats:

  • If you create other global objects outside the context of the isolated code, you need to explicitly shadow the variable like I did with window, document, etc, otherwise the isolated code will be to access it.
  • This won't work in browsers that don't have shadow dom obviously, unless your isolated code doesn't need to interact with the dom at all.
  • You have to be very careful that objects you do give the isolated code access to doesn't contain references to things you don't want it to have access to. Sometimes this is super error prone to do.
  • I'm making this suggestion because its plausible that it works, but I have no idea if there are additional ways to get to things like the window and document objects.
Differ answered 12/10, 2015 at 20:26 Comment(0)
E
1

Is "standard" namespacing an option? Like:

var myNamespace = {};

myNamespace.myFunc = function() { return true; }

This approach is the simplest I can think of and may be the solution to many problems. Although not a real sandbox, it can let the code less error prone.

Enedina answered 30/4, 2012 at 0:35 Comment(0)
L
1

There is a proposal for Realms API, which seems to solve similar problems. It is still under discussion, but there is already a polyfill for it - realms-shim.

Lepanto answered 19/11, 2019 at 20:1 Comment(1)
It's important to note that Realms are not actually meant for running untrusted code. There appears to be another proposal github.com/tc39/proposal-compartments, but I'm not really sure to what extend this allows you to run untrusted code in a secure way.Zebulun

© 2022 - 2024 — McMap. All rights reserved.