Howto: reference a library in JSLint - javascript

I'm using JSLint to do code review/cleanup on a massive series of scripts that interact with an external ERP system.
The problem I am having is that JSLint complains that the methods in the API are used before they are defined.
How can I reference a library within JSLint so that proper lookups are done? I have 493 functions I need to reference so /*Global*/ is not an option.
If this is not possible, is there a better way to do this?
I referenced this post which is asking for something similar, however the answer requires a restructure of the API which is not possible for me to do.

What's the library? Usually, you hope a library has everything namespaced properly so it's not an issue, as in /*global $*/ for jQuery. If the lib is internal and you can edit it, consider doing so (if you want to stick with strict JSLint compliance).
So...
function myFunc1(param1) {
return param1 + "some string1";
}
function myFunc2(param2) {
return param2 + "some string2";
}
becomes...
var fauxNamespace = {
myFunc1: function (param1) {
return param1 + "some string1";
},
myFunc2: function (param2) {
return param2 + "some string2";
}
};
Then you can /*global fauxNamespace*/ and profit. I realize that's non-trivial, but it's The Right Thing to Do (c) Crockford 2008.
Otherwise you're back to the normal "Use JSHint. It's happier because more options," or "JSLint has very clean code. You can actually hack it up yourself to ignore that error if you want," sorts of answers.
What you're looking for is currently on around line 2459:
// If the master is not in scope, then we may have an undeclared variable.
// Check the predefined list. If it was predefined, create the global
// variable.
if (!master) {
writeable = predefined[name];
if (typeof writeable === 'boolean') {
global_scope[name] = master = {
dead: false,
function: global_funct,
kind: 'var',
string: name,
writeable: writeable
};
// But if the variable is not in scope, and is not predefined, and if we are not
// in the global scope, then we have an undefined variable error.
} else {
token.warn('used_before_a');
}
} else {
this.master = master;
}
Is the library public?

Related

How to avoid accidentally implicitly referring to properties on the global object?

Is it possible to execute a block of code without the implicit with(global) context that all scripts seem to have by default? For example, in a browser, would there be any way to set up a script so that a line such as
const foo = location;
throws
Uncaught ReferenceError: location is not defined
instead of accessing window.location, when location has not been declared first? Lacking that, is there a way that such an implicit reference could result in a warning of some sort? It can be a source of bugs when writing code (see below), so having a way to guard against it could be useful.
(Of course, due to ordinary scoping rules, it's possible to declare another variable with the same name using const or let, or within an inner block, to ensure that using that variable name references the new variable rather than the global property, but that's not the same thing.)
This may be similar to asking whether it's possible to stop referencing a property from within an actual with statement:
const obj = { prop: 'prop' };
with (obj) {
// how to make referencing "prop" from somewhere within this block throw a ReferenceError
}
It's known that with should not be used in the first place, but unfortunately it seems we have no choice when it comes to the with(global), which occasionally saves a few characters at the expense of confusing bugs which pop up somewhat frequently: 1 2 3 4 5 6. For example:
var status = false;
if (status) {
console.log('status is actually truthy!');
}
(the issue here: window.status is a reserved property - when assigned to, it coerces the assigned expression to a string)
These sorts of bugs are the same reason that explicit use of with is discouraged or prohibited, yet the implicit with(global) continues to cause issues, even in strict mode, so figuring out a way around it would be useful.
There are some things you need to consider before trying to answer this question.
For example, take the Object constructor. It is a "Standard built-in object".
window.status is part of the Window interface.
Obviously, you don't want status to refer to window.status, but do you want Object to refer to window.Object?
The solution to your problem of it not being able to be redefined is to use a IIFE, or a module, which should be what you are doing anyways.
(() => {
var status = false;
if (!status) {
console.log('status is now false.');
}
})();
And to prevent accidentally using global variables, I would just set up your linter to warn against it. Forcing it using a solution like with (fake_global) would not only have errors exclusively at run time, which might be not caught, but also be slower.
Specifically with ESLint, I can't seem to find a "good" solution. Enabling browser globals allows implicit reads.
I would suggest no-implicit-globals (As you shouldn't be polluting the global scope anyways, and it prevents the var status not defining anything problem), and also not enabling all browser globals, only, say, window, document, console, setInterval, etc., like you said in the comments.
Look at the ESLint environments to see which ones you would like to enable. By default, things like Object and Array are in the global scope, but things like those listed above and atob are not.
To see the exact list of globals, they are defined by this file in ESLint and the globals NPM package. I would would pick from (a combination of) "es6", "worker" or "shared-node-browser".
The eslintrc file would have:
{
"rules": {
"no-implicit-globals": "error"
},
"globals": {
"window": "readonly",
"document": "readonly"
},
"env": {
"browser": false,
"es6": [true/false],
"worker": [true/false],
"shared-node-browser": [true/false]
}
}
If you're not in strict mode, one possibility is to iterate over the property names of the global (or withed) object, and create another object from those properties, whose setters and getters all throw ReferenceErrors, and then nest your code in another with over that object. See comments in the code below.
This isn't a nice solution, but it's the only one I can think of:
const makeObjWhosePropsThrow = inputObj => Object.getOwnPropertyNames(inputObj)
.reduce((a, propName) => {
const doThrow = () => { throw new ReferenceError(propName + ' is not defined!'); };
Object.defineProperty(a, propName, { get: doThrow, set: doThrow });
return a;
}, {});
// (using setTimeout so that console shows both this and the next error)
setTimeout(() => {
const windowWhichThrows = makeObjWhosePropsThrow(window);
with (windowWhichThrows) {
/* Use an IIFE
* so that variables with the same name declared with "var" inside
* create a locally scoped variable
* rather than try to reference the property, which would throw
*/
(() => {
// Declaring any variable name will not throw:
var alert = true; // window.alert
const open = true; // window.open
// Referencing a property name without declaring it first will throw:
const foo = location;
})();
}
});
const obj = { prop1: 'prop1' };
with (obj) {
const inner = makeObjWhosePropsThrow(obj);
with (inner) {
// Referencing a property name without declaring it first will throw:
console.log(prop1);
}
}
.as-console-wrapper {
max-height: 100% !important;
}
Caveats:
This explicitly uses with, which is forbidden in strict mode
This doesn't exactly escape the implicit with(global) scope, or the with(obj) scope: variables in the outer scope with the same name as a property will not be referenceable.
window has a property window, which refers to window. window.window === window. So, referencing window inside the with will throw. Either explicitly exclude the window property, or save another reference to window first.
Somewhat simpler to implement than #CertainPerformance's answer, you can use a Proxy to catch implicit access to everything except window. The only caveat is you can't run this in strict mode:
const strictWindow = Object.create(
new Proxy(window, {
get (target, property) {
if (typeof property !== 'string') return undefined
console.log(`implicit access to ${property}`)
throw new ReferenceError(`${property} is not defined`)
}
}),
Object.getOwnPropertyDescriptors({ window })
)
with (strictWindow) {
try {
const foo = location
} catch (error) {
window.console.log(error.toString())
}
// doesn't throw error
const foo = window.location
}
Notice that even console has to have an explicit reference in order to not throw. If you want to add that as another exception, just modify strictWindow with another own property using
Object.getOwnPropertyDescriptors({ window, console })
In fact, there are a lot of standard built-in objects you may want to add exceptions for, but that is beyond the scope of this answer (no pun intended).
In my opinion, the benefits this offers fall short of the benefits of running in strict mode. A much better solution is to use a properly configured linter that catches implicit references during development rather than at runtime in non-strict mode.
Perhaps slightly cleaner (YMMV) is to set getter traps (like you did), but in a worker so that you don't pollute your main global scope. I didn't need to use with though, so perhaps that is an improvement.
Worker "Thread"
//worker; foo.js
addEventListener('message', function ({ data }) {
try {
eval(`
for (k in self) {
Object.defineProperty(self, k, {
get: function () {
throw new ReferenceError(':(');
}
});
}
// code to execute
${data}
`);
postMessage('no error thrown ');
} catch (e) {
postMessage(`error thrown: ${e.message}`);
}
});
Main "Thread"
var w = new Worker('./foo.js');
w.addEventListener('message', ({data}) => console.log(`response: ${data}`));
w.postMessage('const foo = location');
Another option that may be worth exploring is Puppeteer.
Just use "use strict". More on Strict Mode.

node "require" hoisted to top outside of script -- loses access to variables from outer function

I'm requiring different files at the top of my main script in node. All my require statements are hoisted to the top. This creates a problem because when the methods within those imported scripts are invoked they do not have access to the function within which they are invoked (Because they are inevitably defined outside those functions due to the hoisting issue). Therefore, I must always pass variables in an options object. Has anyone experiences a similar issue? Is there some sort of standard workaround that people use? Thanks!
function outer(){
//let's pretend we're in a node environment
//this required script will get hoisted to the very top and therefore lose access to the "something" variable!
var common = require('../globals/common.js');
var something = "something";
common.printsomething();//will return "something is not defined"
};
outer();
Hm.
I would assume that it'd ultimately be better to pass 'something' to the printsomething method, like so.
common.printfoo('bar'); //returns 'bar'
Typically, what you're doing there isn't how modules in node works. Yes, breaking up a large program into separate files is an excellent way to organize a project, but I'm afraid that I have to say you're doing it wrong here. In the context of 'outer', you could do:
/*script.js*/
var common = require('../globals/common.js');
function outer(str){
common.printsomething(str);//will return "something"
};
var something = 'something';
outer(something);
/*common.js*/
function printthing(str){
console.log(str);
}
module.exports = {
printsomething: function(str){
printthing(str)
}
}
module.js:
module.exports.print = function (data) {
console.log(data);
}
module.exports.add = function (a, b, callback) {
callback(a + b);
}
main.js
var mymodule = require('module');
module.print('Some data'); //Will print "Some data" in the console
module.add(25, 12, function (result) {
console.log(result); //Will print 37
});
As you can see, in main.js, I do not need to know the content of module.js to wrk. that is the goal of modules: put the hard logic somewhere else, to build better code. Modules like async or fs are huge and complex, but I just have to import them to work with it, and don't need to know how it does it.
While building your own module, think of it as a new library of tools, so that you can reuse it in another project without the need to set specific variables to use them. Imagine the chaos it would be if two module were able to get the content of your var something for unrelated goal!
Modules are self contained, to be reusable. A "de hoisting" of thoses would reduce their efficacity.
EDIT:
If you have a lot of environment variable, you can try a pattern where you set them once inside the module, but you have to make sure to provide a way to interact with them.
module:
var data = {};
function set(key, value) {
data[key] = value;
}
function get(key) {
return data[key];
}
function print(key) {
console.log(data[key]);
}
function add(keyA, keyB) {
return data[keyA] + data[keyB];
}
module.exports = {
set: set,
get: get,
print: print,
add: add
};
main.js
var mymod = require('mymod');
mymod.set('data', 'something');
mymod.set('a', 25);
mymod.set('b', 12);
mymod.print('data'); //Print "something"
var c = mymod.add('a', 'b');
console.log(c); //Print 32

Can I prevent passing wrong number of parameters to methods with JS Lint, JS Hint, or some other tool?

I'm new to javascript programming (and scripting languages in general), but I've been using JS Lint to help me when I make syntax errors or accidentally declare a global variable.
However, there is a scenario that JS Lint does not cover, which I feel would be incredibly handy. See the code below:
(function () {
"use strict";
/*global alert */
var testFunction = function (someMessage) {
alert("stuff is happening: " + someMessage);
};
testFunction(1, 2);
testFunction();
}());
Notice that I am passing the wrong number of parameters to testFunction. I don't foresee myself ever in a situation where I would intentionally leave out a parameter or add an extra one like that. However, neither JS Lint nor JS Hint consider this an error.
Is there some other tool that would catch this for me? Or is there some reason why passing parameters like that shouldn't be error checked?
This is not generally possible with any static analysis tool. There are several reasons for this:
In general, JS functions can accept any number of arguments.
Most (all?) linters only work on a single file at a time. They do not know anything about functions declared in other files
There is no guarantee that a property being invoked as a function is the function that you expect. Consider this snippet:
var obj = { myFunc : function(a,b) { ... } };
var doSomething(x) { x.myFunc = function(a) { ... }; };
doSomething(obj);
obj.myFunc();
There is no way to know that myFunc now takes a different number of args after the call to doSomething.
JavaScript is a dynamic language and you should accept and embrace that.
Instead of relying on linting to catch problems that it wasn't intended to I would recommend adding preconditions to your functions that does the check.
Create a helper function like this:
function checkThat(expression, message) {
if (!expression) {
throw new Error(message);
}
}
Then use it like this:
function myFunc(a, b) {
checkThat(arguments.length === 2, "Wrong number of arguments.");
And with proper unit testing, you should never see this error message in production.
It's not natively possible in javascript. You would have to do something like this:
var testFunction = function (someMessage) {
var args = Array.prototype.slice.call(arguments);
if (args.length !== 1) throw new Error ("Wrong number of arguments");
if (typeof args[1] !== string) throw new Error ("Must pass a string");
// continue
};
Paul Irish demoed a hack for this a while back...passing undefined at the end of the parameters...
var testFunction = function (someMessage, undefined) {
alert("stuff is happening: " + someMessage);
};
testFunction("one", "two", "three");
He demos it here...see if it's what your looking for.

What is meant by “leaking” into global scope?

A while ago, I offered-up a JavaScript design pattern (the Module Pattern - see below) that I got from a John Resig example as part of a solution to someone’s question and I received the following comment:
“…that pattern is a bit over
engineered and not that good. Still
leaking into global-scope. and your
not opening yourself to async loaders.
But it is better then just ad-hoc
coding !”
So…
If “leaking” into global scope means “your object gets appended to the browsers window (object)”…then everything already gets appended (globally):
This “leaks” into global scope:
window.jQuery
…just call: window.jQuery and it resolves as a function();
This “leaks” into global scope:
function HelloWorld() { alert(‘Howdy’); }
…just call: window.HelloWorld() and you will get ‘Howdy’.
This “leaks” into global scope:
var myVariable = 10;
…just call: window.myVariable and you will get 10
If the commenter is correct, then all the above “leak” into global-scope. So, personally, I don’t see a way NOT to “leak” into global-scope as even your form controls exists there (as well).
As such, here are my questions…
What is meant by “leaking” into
global-scope?
Why is that bad?
How do you avoid it?
When wanting to create persistent
custom-objects, why is the Module
Pattern (below) bad?
Design patterns let you encapsulate
complex logic, is encapsulation
suddenly bad simply because we’re
writing in JavaScript?
Or...is this commenter simply wrong?
Here is the Module Pattern I Mentioned Above:
<script type="text/javascript">
var myNamespace = (function($) {
var publicInstances = {};
// ***********************
// myObject
publicInstances.myObject = myObject;
function myObject() {
/// <summary>A pointer to this</summary>
var self = this;
this.someProperty = new String();
this.initialize = function() {
/// your code here
}
this.someMethod = function() {
/// your code here
}
self.initialize();
}
return publicInstances;
})(jQuery);
jQuery(document).ready(function() {
// Use would look like
var myInstance = new myNamespace.myObject();
});
</script>
UPDATED:
I’m satisfied with the answers below and want to thank everyone for taking the time to comment.
TO RECAP THE ANSWERS BELOW:
"Leaking" into global-scope occurs when something used in local-scope is unintentionally made available to the global-scope (e.g. the window object). This is bad because it opens the page to potential naming collisions which could result in variables resolving to unexpected values or types.
Intentionally making a variable global is not considered a "leak". However, properly namespacing the object is required to reduce potential for said naming collisions.
You cannot avoid globally-scoped variables, but you can reduce the above risks by using asynchronous-loaders and defining-modules made available in plug-ins like RequireJS or Curl.
"Leaking" into global scope is when something used in a local scope is unintentionally made available to the global scope. That means assigning to a variable not already defined in the current scope:
function myFunction() {
a=1;
}
myFunction();
alert(a);
//-> 1
It's bad because there could be naming collisions resulting in variables with different values/types than expected. It can also lead to a bug in older Internet Explorers when you forget to use the var keyword for a variable used in a for statement.
I wouldn't class intentionally making a variable global as "leaking", because it's more like you're "pouring" it into the global scope. However, this is still often considered bad practice by some (although I think that's a little melodramatic) because there are still potential naming collisions with current properties of the window object, or variables set by other scripts and libraries.
[[Short story]]
Don't make global variables ever and use an async module loader like requirejs or curl
[[Long story]]
That comment was poorly structured.
There is nothing wrong with the module system. I was complaining about using global variables at all. (I still think the full generic module pattern is bloated).
Whether you should avoid all global variables is a different question and I think a matter of style. You can either use an async loader to pass modules around or using window to pass modules around.
What is meant by “leaking” into global-scope?
What I meant was your creating global variables. Minimising the use of global variables is a pattern. In functional style programming it's possible to have zero global variables but this is a different pattern from using global modules.
Why is that bad?
Having any state globally can cause that state to be corrupted.
How do you avoid it?
You can't. You can minimize the amount of global variables though. To avoid having global state completely you can use asynchronous loaders. These define a few global variables for you that you can then use.
When wanting to create persistent custom-objects, why is the Module Pattern (below) bad?
There is nothing wrong with the module pattern. The problem is storing your module globally. The issue is having global namespaces.
Design patterns let you encapsulate complex logic, is encapsulation suddenly bad simply because we’re writing in JavaScript?
Now that I've cleared up the intent of the comment this question isn't really relevant
Or...is this commenter simply wrong?
The comment was poorly phrased at best. I objected to global namespaces rather than modules, but did not state this properly.
The alternative is using asynchronous loaders and defining modules. These can be narrowed down to two global variables. define and require.
require = function(moduleName, callback)
This will get a module and then return it to you.
define = function(obj)
this defines a module.
The concept here is that you multi file code as follows:
// main.js
require([
"foo.js",
"bar.js",
...,
], function(foo, bar, ...) {
// do stuff
});
//foo.js
(function() {
var namespace = modulePatternCode;
...
define(namespace):
})();
//bar.js
(function() {
var namespace = modulePatternCode;
...
define(namespace):
})();
Your module only "leaks" it's namespace holder so it's pretty acceptable.
Loader example using RequireJS:
Define a utilities module in utils.js:
define(function () {
return {
each: function (iterable, callback) {
// ...
},
map: function (iterable, mapper) {
// ...
}
};
});
Use the above module in another module, say math.js:
define([ "utils" ], function (utils) {
return {
sum: function (numbers) {
var sum = 0;
utils.each(numbers, function (n) {
sum += n;
});
return sum;
},
average: function (numbers) {
return this.sum(numbers) / numbers.length;
}
};
});
And you can use math.js in another file, say main.js:
console.log("About to add 1-3");
require([ "math" ], function (math) {
console.log(math.sum([ 1, 2, 3 ]));
});
You can still have namespaces, and still keep them warm and cozy inside modules:
namespace.js:
define([ "foo", "bar", "moo" ] function (foo, bar, moo) {
return {
foo: foo,
bar: bar,
moo: moo
};
});
Then the rest of the modules can use this namespace during definition:
define([ "namespace" ], function (namespace) {
namespace.foo(42);
});
Or at runtime, in some other module:
define(function () {
return {
initialize: function () {
require([ "namespace" ], function (namespace) {
namespace.foo(42);
});
}
};
});
In the usages above, nothing but define and require are global. Of course, these are just illustrative examples, as there are many different flavors of defining/using modules in RequireJS.

Is it possible to sandbox JavaScript running in the browser?

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.

Categories

Resources