Lets say I have one variable var car = 'VW' in index.html
index.html is the binding of my application, from there I access several modules:
<script>
var car = 'VW';
var Initializer = require('./js/initializer');
Initializer.check_requirements();
</script>
Now in initializer I try to access this variable what does not work.
var Initializer = {
check_requirements: function(){console.log(car)};
};
module.exports = Initializer;
However when I attach the variable car to window:
window.car = 'VW';
Then Im able to access it in my module over window.car;
How do I create an global variable car that I can also access in my modules, so that I do not have to attach to window?
Thanks
In a browser context, a "global" variable is in fact a property of the window object, which acts as the global context. The syntax you have in your question, a bare var statement in a script tag in an HTML page, will in fact add that var name as a property to the window. However, since you're using what looks like a CommonJS require statement here, which is not natively supported in most browsers, there's probably a compile step you're not including in your question that's preventing this from working the way you expect.
However, the short answer is, don't do this if you can avoid it. Most applications of global variables are better accomplished with parameters, e.g. Initializer.check_requirements(car);, or in a separate module storing configuration settings, or other approaches.
Related
Background:
I am running a sandboxed iframe which only has the permission "allow-scripts". In the sandbox a script is loaded with custom js provided by the user. Now i want to manage access to global functions/objects like XMLHttpRequest. Currently i achieve that with the following code:
const CLEARED_WINDOW_SCOPE= {};
const scope = { /* Here goes the globals the script should have access too */};
const propertyNames = Object.getOwnPropertyNames(window);
for(let property of propertyNames){
CLEARED_WINDOW_SCOPE[property] = undefined;
}
with(CLEARED_WINDOW_SCOPE){
with(scope){
(function (window, self, frames, globalThis){
${scriptContent}
}).call(scope, scope, scope, scope, scope);
}
}
The script does the following:
Create object with all window property names set to undefined
Create object with all properties the user should have access too
Wrap the user code with two with statements the first clears all globals the second grands access to the defined ones
Wrap the user code with a function that is called with the scope as this value
So far everything works perfectly as excepted.
Problem:
The main problem that i have right now is that when a user defines a function like that:
function aFunction(){
console.log(this);
}
He gains access to the normal window object because the default this value within a function is the window.
Question:
Is it somehow possible to change the default this value of a function to the this value of the surrounding scope. Since the user creates the script i can't wrap all function calls with aFunction.bind(scope). Or is there any other value to prevent the access to the global window object?
Is it somehow possible to change the default this value of a function to the this value of the surrounding scope.
No, but you can do the next best(?) thing: setting it to undefined. Just force strict mode:
"use strict";
// User's code
(function (){
console.log(this)
})();
Also, I'm not a JavaScript security expert, but JS sandboxing is a really complex topic because of things like prototype pollution.
Edit: As CherryDT noted in the comments, this method is not completely secure. Users can still access the <iframe> window by creating a function with the Function constructor, for example. Also, a reference to the main window can be obtained through the <iframe> window (window.parent).
It's maybe OK to use this solution for user-supplied code (since users can just open the DevTools console and start typing), but make sure the code comes from the user and never from a URL search parameter, for example. If the code is completely distrusted, I would recommend to use a well-known library like Google Caja.
One of the views in my Rails 4 app loads a simple javascript to create an instance of the Ace editor once the page loads...
$(function() {
var editor = ace.edit("editor");
}):
In that view, I have a simple ajax button...
<%= button_to "Set Content", {controller: :pages, action: 'set_content'}, remote: true %>
that requests some unobtrusive javascript...
editor.setValue("New content has been set");
but this doesn't work. I'm guessing this doesnt work because editor isn't defined. Since ajax calls fail silently and I don't know how to debug unobtrusive javascript code using the same Chrome tools that I use debug normal javascript code, I can't verify that's the actual problem, but that's my best guess.
I was under the impression that if I write var editor, then is declared as a global variable that my unobtrusive javascript should be able to access.
My questions are...
How can I access global variables inside of my unobtrusive javascript code?
Since global variables are considered evil, is there a better way to access the editor variable from my unobtrusive javascript?
Thanks in advance for your wisdom!
when you use the var keyword inside a function, the scope of that variable is local to the function, so you are incorrect in your assumption about global scope. To make the variable global, declare it outside of your function:
var editor;
$(function(){
editor = ace.edit('editor');
});
alternatively you could simply reference the window object (which is the 'global' object in browsers
$(function(){
window.editor = ace.edit('editor');
});
With regards to avoiding global variables, it is reasonable to use a single global variable (often called 'app', or 'myCompany' or such to provide a namespace that is globally accessible.
var app = {};
$(function(){
app.editor = ace.edit('editor');
});
This at least limits the scope of your new variables so you don't mistakenly overwrite global variables. However the real answer to avoiding global variables lies in the overall architecture of your app, which can't really be addressed without seeing more code. Generally speaking if ALL of the code you write is inside your jquery ready function, then you don't need global variables. Frameworks such as angular, ember, and backbone provide a much better structure for this type of thing than the ad-hoc code common with simple uses of jquery.
$(function() {
var editor = ace.edit("editor");
});
creates a local variable editor within the context of it's callback function. To access it elsewhere, you'll need to make it global window.editor = ace.edit("editor"), or add it to a namespace with something like
window.App = {};
App.editor = ace.edit("editor");
Background
I'm working on tests for the Backbone.dualStorage plugin for Backbone.js that replaces Backbone.sync.
This is a browser plugin, but I'm using jasmine-node to test it. I load the coffeescript source into the node environment to simulate the browser window using vm.createContext:
vm = require 'vm'
fs = require 'fs'
coffee = require 'coffee-script'
backboneDualstoragePath = './backbone.dualstorage.coffee'
source = fs.readFileSync(backboneDualstoragePath, 'utf8')
window = require('./global_test_context.coffee').window
context = vm.createContext window
coffee.eval source, sandbox: context, filename: backboneDualstoragePath
exports.window = context
This works great - in my tests, I can access window.foo, where foo is an attribute exported on the window object in global_test_context.coffee.
I can use Jasmine spies on and redefine methods on any object nested under the window sandbox. Eg:
spyOn(window.Store.prototype, 'create').andReturn(true)
window.Store.create() # The spy is called
window.localsync.create() # This function instantiates a Store object and calls create on it
# The spy is called
However if I try to spy on or otherwise modify the context's direct attributes, the change is not seen inside the sandbox context:
spyOn(window, 'localsync').andReturn(true)
window.localsync() # The spy is called
window.dualsync() # This function references the global localsync
# The original localsync is called
The vm.createContext documentation says:
A (V8) context comprises a global object together with a set of
build-in objects and functions. The optional argument initSandbox
will be shallow-copied to seed the initial contents of the global object used by the context.
So it sounds like it copies the attributes from my window variable into the vm context. When I spy on or otherwise modify window after this point, I am working with the vm context, which I export as an attribute named window. Therefore I think that the paragraph above is irrelevant, but I wanted to include it in case I'm wrong.
The Question
The flow of events boils down to this:
window = vm.createContext({globalVariables: forTesting...})
# similiar to vm.runInContext(backboneDualstorageSource, window)
coffee.eval(backboneDualstorageSource, sandbox: window)
# localsync and dualsync are both defined in the backboneDualstorageSource
spyOn(window, 'localsync')
window.dualsync() # calls the original localsync instead of the spy
Why is it that after modifying attributes on the vm context, references to those "global" attributes/functions inside the vm don't change? I want to understand this.
How can I work around this so that I can modify/spyOn globals in the browser script I'm testing?
Feel free to look at the source to get a better idea of how things are actually written compared to the snippets in this question.
Edit
I was able to work around this issue by creating the spy inside an eval that runs in the context, like the rest of the tested code. See https://github.com/nilbus/Backbone.dualStorage/commit/eb6c2b21
Could someone explain why I'm able to modify global variables within the context but not outside the context, even though I have access to them?
Modifying context after eval is not affecting already evaluated scripts, if I understand your issue correctly. If you don't need to add new members to context, and just need to modify existing ones, use getters, which is working fine in latest node (0.8.x for now).
don't go down one level with the global key, try this:
window = vm.createContext({variablesForTesting.. })
I am currently coding in this way:
<script type="text/javascript">
var linkObj;
Is this a safe way to store data? My concern is what if a jQuery or other plug-in was to also use the variable linkObj. Also if I declare my variable like this then can it also be seen by other functions in scripts located in other js files that I include?
$(document).ready(function(){
var linkObj;
});
as long as you use the var keyword, any variable defined in that scope won't be accessible by other plugins.
I you declare a variable this way it will be accessible to all scripts running on the page.
If you just want to use it locally, wrap it in a function:
(function() {var linkObj; ... })()
However, this way nothing outside of the function will be able to access it.
If you want to explicitly share certain variables between different scripts, you could also use an object as a namespace:
var myProject = {}
myProject.linkObj = ...
This will minimize how many global names you have to rely on.
Wrap it in a closure:
<script type="text/javascript">
(function() {
var linkObj;
// Rest of your code
})();
</script>
This way no script outside your own will have access to linkObj.
Is this a safe way to store data?
This is not storing data per se, it's only declaring a variable in a script block in what I assume is an HTML page. When you reload the page in the future, it will not hold previous values.
My concern is what if a jQuery or other plug-in was to also use the variable linkObj.
That's a valid concern, like others have pointed out. However, you would expect plugins not to rely on scope outside the plug-in. This shouldn't impact a lot as good plug-in design would likely prevent this from happening.
Also if I declare my variable like this then can it also be seen by other functions in scripts located in other js files that I include?
Yes. As long as their execution is triggered after your script block gets loaded. This normally follows the order in which your script declaration appears in the page. Or regardless of the order they appear on the page if they are executed, for example, after the jQuery DOM 'ready' event.
It's common to hear that is good to avoid 'global namespace pollution', which relates to this concern. To accomplish that you can use a function to contain code, and directly invoke that function in your script block.
(function () {
var a = 1; // the scope is within the function
alert('The variable a is equal to: ' + a);
}) (); // the parenthesis invoke the function immediately
Is it possible to sandbox javascript module from DOM manipulation? Fo example
var Core = {
register: function(config){config.init()},
publicApi: {
msgbox: function(msg){alert(msg)}
}
}
Core.register({
name: 'testmodule',
init: function(){
/* from there i want to see only function defined in Core.publicApi, no jQuery direct access, no DOM */
}
});
Well, somewhat: you can sandbox the function, but its callees will also be sandboxed. That means even stuff in Core.publicApi won't be able to access document and the like. Or, at least, there's no bulletproof sandboxing in Javascript that will allow that bridging.
You can cripple what's available by temporarily overwriting the window variable:
var window = {"Core": Core};
But then no global variable (even such as alert) will exist for the callees. This will most likely break your API.
You can add another member (like _unsandboxed or whatever) into the new window variable to allow your API to access the members. But, as I said, it's not bulletproof since sandboxed functions can still access _unsandboxed.