I have my HTML setup like this:
<script type="module" src="main.js"></script>
and all the ES6 modules work fine. The only problem is I now can't refer to anything from within DevTools (like using the Console and typing in a variable to see it's value or using a function manually).
How do I import modules whilst being able to use the DevTools? Thanks!
One way to make a variable accessable within DevTools is to create it on the window object:
// Variable in your module
var importantNumber = 1;
window.importantNumber = importantNumber;
This method works fine if you just have a couple of variables, but if you need to have access to a lot more variables within DevTools, I would recommend you go to the sources-tab in DevTools, search for your module and adding a breakpoint. When the execution pauses, you have access to all the variables within that module on the DevTools console.
If you want to be able to refer to variables created within the module from the console's global scope, you'll have to deliberately expose each such variable that you want to be visible from the console. Either assign each variable to window (probably not a good idea - the whole point of modules is to make things more modular, without global pollution), or perhaps assign a single object to window, to which you assign module variables. For example:
// in the console:
setTimeout(() => {
window.myModule.foo();
console.log(window.myModule.bar);
});
<script type="module">
function foo() {
console.log('doing foo');
}
const bar = 'a string bar';
const thisModule = { foo, bar };
window.myModule = thisModule;
// If you ever reassign variables, you'll have to reassign them on thisModule too
// or, only reference and reassign properties of thisModule, rather than create independent variables
</script>
For anyone else interested, if you're comfortable with it, use a bundler like Webpack. I don't believe (at least at this point) that the browser will by itself be able to use the DevTools on modules (the other solutions are quite janky, and aren't fantastic to work with).
Hopefully in the future, when all major browsers will be able to support ES6 modules without a bundler, we'll be able to use DevTools.
Using a Helper
I personally use a little helper function in development that allows me to expose a bunch a variables in a single expression. For example, it makes the following two blocks equivalent:
window.playerOne = playerOne;
window.someClass = someClass;
window.localData = localData;
globalize({playerOne, someClass, localData});
The helper looks like this:
const globalize = function(variables) {
Object.entries(variables).forEach(([name, value]) => window[name] = value);
};
Background
I just learned that calling keys(window) (or Object.keys(window)) in the DevTools console reveals variables in the global scope (source). I called that code on the StackOverflow page and got this result:
Variable i got my attention as it seems to be in the global scope by a mistake. I tried locating code that is responsible for declaring i, but it turned out to be cumbersome (there is a lot of code and a lot of is).
Question
Getting console warnings that say
Global variable "i" created (main.js:342)
could be useful. How can I implement that feature?
Research
I figured that I need some kind of an event whenever new variable is created.
We do have setters in JavaScript. However, creating a setter requires that you provide a property name. Since I want to monitor all properties I can't really use that.
__noSuchMethod__ (MDN) would be perfect but it only covers methods (and there is no __noSuchProperty__ method).
Object.observe (HTML5 Rocks) doesn't reveal anything about the code that created the property (console.trace() gives me only the name of the observer function).
Object.prototype.watch (MDN) - same as the setter, you have to specify a property name.
Calling Object.preventExtensions(window) (MDN) causes errors with a nice stack trace whenever new global variable is created. The problem with this solution is that it interferes with the script execution and may change its behaviour. It also doesn't allow me to catch the error and format it properly.
Notes
I know about jshint/jslint and I still think that catching these declarations in the runtime could be useful.
I don't care about i variable on the SO page that much, you can probably find the declaration using setters. My question concerns the general solution for this problem.
IMO you have two decent options:
JSHint.
Strict mode.
Both will yell at you when you leak a global. Strict mode will probably be better for your usecases.
You've definitely done your homework, and you thought of all the things I would have thought of and, as you discovered, none of them fit.
The only way I can think of doing is to just monitor the global object (this example is using window as the global object: modify accordingly for Node or another JavaScript container). Here's an example that monitors new globals and deleted globals (if you don't need to monitor deletions, you can remove that functionality):
var globals = Object.keys(window);
var monitorGlobalInterval = 50;
setInterval(function(){
var globalsNow = Object.keys(window);
var newGlobals = globalsNow.filter(function(key){
return globals.indexOf(key)===-1;
});
var deletedGlobals = globals.filter(function(key){
return globalsNow.indexOf(key)===-1;
});
newGlobals.forEach(function(key){
console.log('new global: ' + key);
});
deletedGlobals.forEach(function(key){
console.log('global deleted: ' + key);
});
globals = globalsNow;
}, monitorGlobalInterval);
See it in action here: http://jsfiddle.net/dRjP9/2/
You can try this method to get a list of global variables that you've created:
(function(){
var differences = {},
ignoreList = (prompt('Ignore filter (comma sep)?', 'jQuery, Backbone, _, $').split(/,\s?/) || []),
iframe = document.createElement('iframe'),
count = 0; ignoreList.push('prop');
for (prop in window) {
differences[prop] = {
type: typeof window[prop],
val: window[prop]
}; count++;
}
iframe.src = 'about:blank';
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe = iframe.contentWindow || iframe.contentDocument;
for (prop in differences) {
if (prop in iframe || ignoreList.indexOf(prop) >= 0) {
delete differences[prop];
count--;
}
}
console.info('Total globals: %d', count);
return differences;
})();
In JavaScript when i say window.SomeData = 'whatever', where does it get saved in the browser? I thought it gets saved in the viewstate but it doesnt. Also how much security concern is it to save some data in window.someKey. I am not talking about username or password storage but some general data like PK values of some records.
--Edit--
The reason i am asking this is because i have a page with 5 tabs and each tabs gets loaded by an AJAX call. I need to save the data that comes back from AJAX request and currently i am using window.somekey to save it.
It gets saved in the global object window. That object is not very secure and its lifetime is for that page.
It doesn't get saved anywhere, it is exactly the same as using a global variable:
var SomeData = "whatever"; //done in global scope obviously
window.SomeData === "whatever" //true
Nowhere, effectively. Data stored in random fields of the window object is not saved.
The annoying answer is its saved in memory! Its no different than having
var j = {}
j.SomeData = 'Whatever'
window is just a global variable containing all the information about window (such as the documents DOM)
window can have data saved like the j variable above. Its no less safe than saving any piece of information ANYWHERE else in js. The only risk you have is that the items namespace may be used by another library.
So if i were you i would do this. (use this as a guide to writing good JS http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/)
(function(MyLibrary, $, undefined) {
var stuff;
MyLibrary.SomeData = 5;
}(window.MyLibrary = window.MyLibrary || {}, jQuery)); // or moo tools
alert(window.MyLibrary.SomeData); // 5!
It's generally good practice to use an object as namespace for functions and variables specific for web application. For example:
var site = {};
site.someData = 'whatever';
site.initGallery = function() {
//
};
Declaration of variables in global namespace (in client-side JS, window is global namespace) is called global-namespace pollution and is generally undesirable and unrecommended since it can lead to naming conflicts either with other libraries that also pollute global namespace or, even worse, with browser's native global objects.
I'm wondering if it's possible to sandbox JavaScript running in the browser to prevent access to features that are normally available to JavaScript code running in an HTML page.
For example, let's say I want to provide a JavaScript API for end users to let them define event handlers to be run when "interesting events" happen, but I don't want those users to access the properties and functions of the window object. Am I able to do this?
In the simplest case, let's say I want to prevent users calling alert. A couple of approaches I can think of are:
Redefine window.alert globally. I don't think this would be a valid approach because other code running in the page (i.e., stuff not authored by users in their event handlers) might want to use alert.
Send the event handler code to the server to process. I'm not sure that sending the code to the server to process is the right approach, because the event handlers need to run in the context of the page.
Perhaps a solution where the server processes the user defined function and then generates a callback to be executed on the client would work? Even if that approach works, are there better ways to solve this problem?
Google Caja is a source-to-source translator that "allows you to put untrusted third-party HTML and JavaScript inline in your page and still be secure."
Have a look at Douglas Crockford's ADsafe:
ADsafe makes it safe to put guest code (such as third party scripted advertising or widgets) on any web page. ADsafe defines a subset of JavaScript that is powerful enough to allow guest code to perform valuable interactions, while at the same time preventing malicious or accidental damage or intrusion. The ADsafe subset can be verified mechanically by tools like JSLint so that no human inspection is necessary to review guest code for safety. The ADsafe subset also enforces good coding practices, increasing the likelihood that guest code will run correctly.
You can see an example of how to use ADsafe by looking at the template.html and template.js files in the project's GitHub repository.
I created a sandboxing library called jsandbox that uses web workers to sandbox evaluated code. It also has an input method for explicitly giving sandboxed code data it wouldn't otherwise be able to get.
The following is an example of the API:
jsandbox
.eval({
code : "x=1;Math.round(Math.pow(input, ++x))",
input : 36.565010597564445,
callback: function(n) {
console.log("number: ", n); // number: 1337
}
}).eval({
code : "][];.]\\ (*# ($(! ~",
onerror: function(ex) {
console.log("syntax error: ", ex); // syntax error: [error object]
}
}).eval({
code : '"foo"+input',
input : "bar",
callback: function(str) {
console.log("string: ", str); // string: foobar
}
}).eval({
code : "({q:1, w:2})",
callback: function(obj) {
console.log("object: ", obj); // object: object q=1 w=2
}
}).eval({
code : "[1, 2, 3].concat(input)",
input : [4, 5, 6],
callback: function(arr) {
console.log("array: ", arr); // array: [1, 2, 3, 4, 5, 6]
}
}).eval({
code : "function x(z){this.y=z;};new x(input)",
input : 4,
callback: function(x) {
console.log("new x: ", x); // new x: object y=4
}
});
An improved version of RyanOHara's web workers sandbox code, in a single file (no extra eval.js file is necessary).
function safeEval(untrustedCode)
{
return new Promise(function (resolve, reject)
{
var blobURL = URL.createObjectURL(new Blob([
"(",
function ()
{
var _postMessage = postMessage;
var _addEventListener = addEventListener;
(function (obj)
{
"use strict";
var current = obj;
var keepProperties =
[
// Required
'Object', 'Function', 'Infinity', 'NaN', 'undefined', 'caches', 'TEMPORARY', 'PERSISTENT',
// Optional, but trivial to get back
'Array', 'Boolean', 'Number', 'String', 'Symbol',
// Optional
'Map', 'Math', 'Set',
];
do
{
Object.getOwnPropertyNames(current).forEach(function (name)
{
if (keepProperties.indexOf(name) === -1)
{
delete current[name];
}
});
current = Object.getPrototypeOf(current);
}
while (current !== Object.prototype)
;
})(this);
_addEventListener("message", function (e)
{
var f = new Function("", "return (" + e.data + "\n);");
_postMessage(f());
});
}.toString(),
")()"],
{type: "application/javascript"}));
var worker = new Worker(blobURL);
URL.revokeObjectURL(blobURL);
worker.onmessage = function (evt)
{
worker.terminate();
resolve(evt.data);
};
worker.onerror = function (evt)
{
reject(new Error(evt.message));
};
worker.postMessage(untrustedCode);
setTimeout(function ()
{
worker.terminate();
reject(new Error('The worker timed out.'));
}, 1000);
});
}
Test it:
https://jsfiddle.net/kp0cq6yw/
var promise = safeEval("1+2+3");
promise.then(function (result) {
alert(result);
});
It should output 6 (tested in Chrome and Firefox).
As mentioned in other responces, it's enough to jail the code in a sandboxed iframe (without sending it to the server-side) and communicate with messages.
I would suggest to take a look at a small library I created mostly because of the need to providing some API to the untrusted code, just like as described in the question: there's an opportunity to export the particular set of functions right into the sandbox where the untrusted code runs. And there's also a demo which executes the code submitted by a user in a sandbox:
http://asvd.github.io/jailed/demos/web/console/
I think that js.js is worth mentioning here. It's a JavaScript interpreter written in JavaScript.
It's about 200 times slower than native JavaScript, but its nature makes it a perfect sandbox environment. Another drawback is its size – almost 600 KB, which may be acceptable for desktops in some cases, but not for mobile devices.
All the browser vendors and the HTML5 specification are working towards an actual sandbox property to allow sandboxed iframes -- but it's still limited to iframe granularity.
In general, no degree of regular expressions, etc. can safely sanitise arbitrary user provided JavaScript as it degenerates to the halting problem :-/
An ugly way, but maybe this works for you:
I took all the globals and redefined them in the sandbox scope, as well I added the strict mode so they can't get the global object using an anonymous function.
function construct(constructor, args) {
function F() {
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
// Sanboxer
function sandboxcode(string, inject) {
"use strict";
var globals = [];
for (var i in window) {
// <--REMOVE THIS CONDITION
if (i != "console")
// REMOVE THIS CONDITION -->
globals.push(i);
}
globals.push('"use strict";\n'+string);
return construct(Function, globals).apply(inject ? inject : {});
}
sandboxcode('console.log( this, window, top , self, parent, this["jQuery"], (function(){return this;}()));');
// => Object {} undefined undefined undefined undefined undefined undefined
console.log("return of this", sandboxcode('return this;', {window:"sanboxed code"}));
// => Object {window: "sanboxed code"}
https://gist.github.com/alejandrolechuga/9381781
An independent JavaScript interpreter is more likely to yield a robust sandbox than a caged version of the built-in browser implementation.
Ryan has already mentioned js.js, but a more up-to-date project is JS-Interpreter. The documentation covers how to expose various functions to the interpreter, but its scope is otherwise very limited.
As of 2019, vm2 looks like the most popular and most regularly-updated solution to running JavaScript in Node.js. I'm not aware of a front-end solution.
With NISP you'll be able to do sandboxed evaluation.
Though the expression you write is not exactly JavaScript code, instead you'll write S-expressions. It is ideal for simple DSLs that doesn't demand extensive programming.
Suppose you have code to execute:
var sCode = "alert(document)";
Now, suppose you want to execute it in a sandbox:
new Function("window", "with(window){" + sCode + "}")({});
These two lines when executed will fail, because "alert" function is not available from the "sandbox"
And now you want to expose a member of window object with your functionality:
new Function("window", "with(window){" + sCode + "}")({
'alert':function(sString){document.title = sString}
});
Indeed you can add quotes escaping and make other polishing, but I guess the idea is clear.
Where is this user JavaScript code coming from?
There is not much you can do about a user embedding code into your page and then calling it from their browser (see Greasemonkey). It's just something browsers do.
However, if you store the script in a database, then retrieve it and eval() it, then you can clean up the script before it is run.
Examples of code that removes all window. and document. references:
eval(
unsafeUserScript
.replace(/\/\/.+\n|\/\*.*\*\/, '') // Clear all comments
.replace(/\s(window|document)\s*[\;\)\.]/, '') // Removes window. Or window; or window)
)
This tries to prevent the following from being executed (not tested):
window.location = 'http://example.com';
var w = window;
There are a lot of limitations you would have to apply to the unsafe user script. Unfortunately, there isn't any 'sandbox container' available for JavaScript.
I've been working on a simplistic JavaScript sandbox for letting users build applets for my site. Although I still face some challenges with allowing DOM access (parentNode just won't let me keep things secure =/), my approach was just to redefine the window object with some of its useful/harmless members, and then eval() the user code with this redefined window as the default scope.
My "core" code goes like this... (I'm not showing it entirely ;)
function Sandbox(parent){
this.scope = {
window: {
alert: function(str){
alert("Overriden Alert: " + str);
},
prompt: function(message, defaultValue){
return prompt("Overriden Prompt:" + message, defaultValue);
},
document: null,
.
.
.
.
}
};
this.execute = function(codestring){
// Here some code sanitizing, please
with (this.scope) {
with (window) {
eval(codestring);
}
}
};
}
So, I can instantiate a Sandbox and use its execute() function to get code running. Also, all new declared variables within eval'd code will ultimately bound to the execute() scope, so there will not be clashing names or messing with existing code.
Although global objects will still be accessible, those which should remain unknown to the sandboxed code must be defined as proxies in the Sandbox::scope object.
You can wrap the user's code in a function that redefines forbidden objects as parameters -- these would then be undefined when called:
(function (alert) {
alert ("uh oh!"); // User code
}) ();
Of course, clever attackers can get around this by inspecting the JavaScript DOM and finding a non-overridden object that contains a reference to the window.
Another idea is scanning the user's code using a tool like JSLint. Make sure it's set to have no preset variables (or: only variables you want), and then if any globals are set or accessed do not let the user's script be used. Again, it might be vulnerable to walking the DOM -- objects that the user can construct using literals might have implicit references to the window object that could be accessed to escape the sandbox.