I want to create an HTML+JS environment where user can enter and run arbitrary JavaScript code which will be executed in context of given jail object. I've set up a playground to illustrate what I have so far.
This one does a somewhat decent job:
Basic evaluation works:
Input: 2 + 2
Output: 4
this returns jail object
Input: this
Output: [object Object]
this.hello() runs expected method
Input: this.hello()
Output: hello world!
User can set up their own functions and execute them later:
Input: this.foo = function() { return 42; }
Output: function () { return 42; }
Input: this.foo()
Ouput: 42
Trying to access some object that I want to stay "hidden" from jail context fails:
Input: hidden
Output: ReferenceError: hidden is not defined
However, it completely fails to hide globally accessible properties, such as window or document for user:
Input: window
Current output: [object Window]
Desired output: ReferenceError: window is not defined
The best solution I've came up so far is to just fill up all the global variable I can think of with undefined or null right in the Jail object declaration, as illustrated in updated version. This way they seem to be lost forever inside the scope of jail and user won't be able to access them. My questions:
Am I right? Is this safe enough?
Is there any better way, i.e. to really undefine global stuff in certain scope instead of rewriting them with some placeholder values?
If it’s client-side and you can guarantee a modern browser, use web workers instead; they’re much safer, and you can also stop infinite loops from tying up the main thread, and implement timeouts by calling Worker#terminate.
Start up a new worker for each execution:
var worker = new Worker('path/to/evaluator.js');
Receive messages from the worker:
worker.onmessage = function (e) {
console.log(e.data);
};
Send over the code to execute:
worker.postMessage(someCode);
In the worker, listen:
onmessage = function (e) {
postMessage(eval(e.data));
};
And make sure to call terminate after receiving the message, too, because the worker can call postMessage itself. (You can prevent that, but there’s really no point.)
Web workers don’t have access to anything in the main execution context, they run on another thread, and they’re implemented by the browser, so they’re much safer than the typical delete-dangerous-things sandbox.
They do, however, have access to XMLHttpRequest; see Is It Possible to Sandbox JavaScript Running In the Browser? for other approaches if this is a problem.
Related
Might be a silly question but it still got me a bit stuck, not being 100% sure of the answers.
So I have an index.html file which calls a function in an example.js-file (just adding it for clarification):
function sinusGraph() {
var plotstart = 0,
plotrange = 20,
stepsize = 0.5; // not in use right now
var yValues, xValues;
function sinusValues(startinput, stopinput)
{
return d3.range(startinput, stopinput+1).map(function(i)
{
return Math.sin(i);
})
};
function xAxisValues(startinput, stopinput)
{
return d3.range(startinput, stopinput+1).map(function(i)
{
return i;
})
};
xValues = xAxisValues(plotstart, plotrange);
yValues = sinusValues(plotstart, plotrange); };
Writing, for example, "xValues" with the variables declared in the browser's returns "xValues is not defined(...)".
Removing "var xValues" letting it be a global variable does return the value.
My questions:
The browser's Tool Console can't see non-global variables within functions?
If that is the case, then is this a good tool to look for potential global variables that you have created by mistake?
Is there any way to view these variables in the browser's tool console, other than using console.log(myVariable) within the function where it is declared?
The browser's Tool Console can't see non-global variables within functions?
Yes.
Here is why it can't work: Local variables of a function only exist while the function is running. Code you type into the console will either be executed before or after the function is executed.
Even if you were able to access local variables after a function was executed:
which value would you expect to get when you type varname in the console? The inital value, the last value or all values?
what if multiple functions have a local variable with the same name?
what if the same function was already executed multiple times?
You can only inspect the current state of your application.
If that is the case, then is this a good tool to look for potential global variables that you have created by mistake?
No. You should use a linter such as ESLint that warns you if you forget e.g. a var declaration. Also enable strict mode: Assignments to undeclared variables will throw an error then.
Is there any way to view these variables in the browser's tool console, other than using console.log(myVariable) within the function where it is declared?
Set a break point in your code, either through the devtools or via debugger. Code execution will pause at the breakpoint and the console has access to everything that is accessible at the breakpoint.
The console has to run the code some scope (and it can't access a scope defined by a function that isn't running anyway).
Set a breakpoint inside the function, to pause execution there. When that breakpoint is triggered, the console's scope will be to that function.
I'm using Jasmine standalone https://github.com/jasmine/jasmine/releases
I have declared a global variable global_song in SpecRunner.html (I can access it from chrome console so it's truly global) which includes script where I am trying to concatenate global_song to "should be able to play Song " :
it("should be able to play Song " + global_song, function() {
player.play(song);
expect(player.currentlyPlayingSong).toEqual(global_song);
//demonstrates use of custom matcher
expect(player).toBePlaying(song);
});
Why it cannot access global_song variable ?
Update : expect(player.currentlyPlayingSong).toEqual(global_song) works whereas it("should be able to play Song " + global_song doesn't work.
Where have you defined global_song? If you did that in the beforeEach() function this behaviour would make sense as the code in the describe block (which attempts to define your it() function) gets executed before the beforeEach() as described in this other SO answer.
Well i suppose your global_song created after executing of
player.play(song);
That's way it's not available in test as first parameter of it and available after executing player.play in expect(player.currentlyPlayingSong).toEqual(global_song) assertion.
Try to add simple assignment to global_song separatelly from player.play to verify that it's available before player.play executed:
window.global_song = 'value'
Just take a look on that sample to illustrate the main possible candidate of problem:
function foo(){
window['bar'] = 'bar';
}
console.log(window.bar); // Undefined
foo(); // now window contain bar variable.
console.log(window.bar); // 'bar'
I have several script blocks depend on each other. I need to perform them in one scope.
My attempt:
var scopeWrapper = {};
with(scopeWrapper) {
(function() {
this.run = function(code) {
eval(code);
};
}).call(scopeWrapper);
}
scopeWrapper.run('function test() { alert("passed"); }');
scopeWrapper.run('test();');
I get 'test is not defined' error. It seems that the code is executed in different scopes.
Why is this happening?
Edit: Bergi pointed out my original answer was wrong, he is correct. Since eval runs in its own scope and the function constructor still runs in function scope according to the spec this is not possible with either.
While I have done this sort of thing myself several times with node.js using the vm module where you get much finer grain of control over where your code executes, it seems browsers require a different approach.
The only way you can share variables in such a way is to do so in the global scope of JavaScript execution (possibly, in an iframe). One way you could do this is script tag injection.
function run(code){
var sc = document.createElement("script");
sc.setAttribute("type","text/javascript");
sc.innerHTML = code;
document.body.appendChild(sc);
}
run("var x = 5");
run("document.write(x)");
(here is this code in action)
As for the scope wrapper, instead of injecting them in the same frame inject them in another iframe. That will scope their window object to that iframe and will allow you to share context.
I humbly apologize for my previous answer, I misread the spec. I hope this answer helps you.
I'm leaving my previous answer here because I still believe it provides some insight into how eval and the Function constructor work.
When running code in non-strict mode eval runs in the current context of your page
After your function declaration is done, the scope it was declared in dies, and with it the function.
Consider using the Function constructor and then .calling it
In your case that would be something like:
var scopeWrapper = {};
scopeWrapper.run = function(code){
var functionToRun = new Function(code);
functionToRun.call(scopeWrapper);
}
scopeWrapper.run('this.test = function() { alert("passed"); }');
scopeWrapper.run("this.test()")
Here is a reference directly from the spec:
If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then,
Initialize the execution context as if it was a global execution context using the eval code as C as described in 10.4.1.1.
If this code is run in the node.js consider using the vm module. Also note that this approach is still not secure in the way it'll allow code you run to change your code.
test only exists in the scope of this.run and only at call time :
// global scope
(function(){
// local scope (equivalent of your "run" function scope)
eval('function f(){};');
console.log(f); // prints "function f(){}"
})();
console.log(f); // prints "ReferenceError: f is not defined"
Each call of run creates a new scope in which each code is evaluated separately.
I am writing a node app that needs to remember data across connection iterations of the createServer() callback. Is there a simple way that doesn't involve databases or file r/w? I've sofar attempted creating objects in the respective modules and even main script while passing them into various response handlers, however for every connection they are flushed.
What I mean by that:
require('http').createServer(function(req,res){
route(req,res,object);
}).listen(cp=80);
object={variable:0}
function route(req,res,object){
res.end();
console.log(object.variable);
object.variable=Math.floor(Math.random()*100);
}
console.log is unsurprisingly throws 0 every connection in this case. Is there any way to create global variables, not in the sense of being available across modules, but persistent unlike var's?
Each module in Node has its own scope, so no, var Foo; does not create a global variable Foo. Use global object from inside the modules.
UPDATE:
require('http').createServer(function(req,res){
route(req,res,object);
}).listen(cp=8080);
object={variable:0}
global.foo = 'Bar'; // I added this
function route(req,res,object){
res.end();
console.log(object.variable);
console.log("foo = %s", global.foo); // I added this too
object.variable=Math.floor(Math.random()*100);
}
And it logs "foo = Bar" as expected as well.
I am writing a web application that has a static outer "shell" and a dynamic content section. The dynamic content section has many updates as users navigate the system. When a new content block is loaded, it may also optionally load another JavaScript file. In the name of good housekeeping, I remove script blocks from the DOM that apply to old content blocks, since that JavaScript is no longer needed.
The problem comes next, when I realized that although I have removed the <script> element from the DOM, the JavaScript that was previously evaluated is still available for execution. That makes sense of course, but I'm worried that it may cause a memory leak if the users navigate to a lot of different sections.
The question then, is should I be worried about this situation? If so, is there a way to force the browser to cleanup stale JavaScript?
<theory>You could go with a more object-oriented approach, and build the model in a way that each block of javascript blocks come in as their own objects, with their own methods. Upon unloading it, you simply set that object to null.</theory>
(This is fairly off-the-cuff.)
Memory use is indeed an issue you need to be concerned with in the current browser state of the art, although unless we're talking about quite a lot of code, I don't know that code size is the issue (it's usually DOM size, and leftover event handlers).
You could use a pattern for your loadable modules that would make it much easier to unload them en mass -- or at least, to let the browser know it can unload them.
Consider:
window.MyModule = (function() {
alert('This happens the moment the module is loaded.');
function MyModule() {
function foo() {
bar();
}
function bar() {
}
}
return MyModule;
})();
That defines a closure that contains the functions foo and bar, which can call each other in the normal way. Note that code outside functions runs immediately.
Provided you don't pass out any references to what's inside the closure to anything outside it, then window.MyModule will be the only reference to that closure and its execution context. To unload it:
try {
delete window.MyModule;
}
catch (e) {
// Work around IE bug that doesn't allow `delete` on `window` properties
window.MyModule = undefined;
}
That tells the JavaScript environment you're not using that property anymore, and makes anything it references available for garbage collection. When and whether that collection happens is obviously implementation-dependent.
Note that it will be important if you hook event handlers within the module to unhook them before unloading. You could do that by returning a reference to a destructor function instead of the main closure:
window.MyModule = (function() {
alert('This happens the moment the module is loaded.');
function foo() {
bar();
}
function bar() {
}
function destructor() {
// Unhook event handlers here
}
return destructor;
})();
Unhooking is then:
if (window.MyModule) {
try {
window.MyModule();
}
catch (e) {
}
try {
delete window.MyModule;
}
catch (e) {
// Work around IE bug that doesn't allow `delete` on `window` properties
window.MyModule = undefined;
}
}
If you save the evaluated code in namespaces, such as:
var MYAPP = {
myFunc: function(a) { ... }
}
"Freeing" the whole thing should be as simple as setting MYPP to some random value, ala
MYAPP = 1
This does depend on there being no other means of referencing the variable, which isn't trivial
How about loading the JS files into an iframe? Then (in theory, never tested it myself) you can remove the iframe from the DOM and remove the "memory" it's using.
I think... or I hope...
If you are worried about memory leaks then you will want to make certain that there is no event handlers in the code you want removed referring to the still existing dom tree.
It may be that you need to keep a list of all event handlers your code added, and before unloading, go through and remove the event handlers.
I have never done it that way, I always worry about when I remove nodes that there is still a reference.
Here is a good article on javascript memory leaks:
http://javascript.crockford.com/memory/leak.html
JavaScript interpreters have garbage collectors. In other words, if you don't reference anything, it won't be keeping them around.
One of the reasons why it is good to use JSON with a callback function (JSONP).
example, if you HTTP response for each JS is:
callback({status: '1', resp: [resp here..]});
And if callback() does not create a reference to the JSON object passed in as an argument, it will be garbage collected after the function completes.
If you really need to make a reference, then you probably need that data around for some reason - otherwise you would/should NOT have referenced it in the first place.
The methods mentioned to namespace objects just creates a reference that will be persisted until the reference count comes to 0. In other words, you have to track every reference and delete it later, which can be hard when you have closures and references from DOM lying around. Just one reference will keep the object in memory, and some simple operations may create references without you realizing it.
Nice discussion. Clears up a lot of things. I have another worry, though.
If I bind window.MyModule.bar() to an event, what happens if the event accidentally gets triggered after window.MyModule is deleted? For me, the whole point of namespacing and separating js into dynamically loaded modules is to avoid triggering event handlers cross-module by mistake.
For example, if I do (excuse my jQuery):
$('.some-class').click(window.MyModule.bar);
What happens if I delete window.MyModule, load another module, and click on an element which accidentally has a class called some-class?