source: Dev/trunk/src/client/dojox/secure/sandbox.js @ 532

Last change on this file since 532 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 11.8 KB
Line 
1dojo.provide("dojox.secure.sandbox");
2dojo.require("dojox.secure.DOM");
3dojo.require("dojox.secure.capability");
4dojo.require("dojo.NodeList-fx");
5dojo.require("dojo._base.url");
6
7(function() {
8        var oldTimeout = setTimeout;
9        var oldInterval = setInterval;
10        if({}.__proto__){
11                // mozilla has unsafe methods on array
12                var fixMozArrayFunction = function (name) {
13                        var method = Array.prototype[name];
14                        if(method && !method.fixed){
15                                (Array.prototype[name] = function () {
16                                        if (this == window) {
17                                                throw new TypeError("Called with wrong this");
18                                        }
19                                        return method.apply(this, arguments);
20                                }).fixed = true;
21                        }
22                };
23                // these are not safe in mozilla
24                fixMozArrayFunction('concat');
25                fixMozArrayFunction('reverse');
26                fixMozArrayFunction('sort');
27                fixMozArrayFunction("slice");
28                fixMozArrayFunction("forEach");
29                fixMozArrayFunction("filter");
30                fixMozArrayFunction("reduce");
31                fixMozArrayFunction("reduceRight");
32                fixMozArrayFunction("every");
33                fixMozArrayFunction("map");
34                fixMozArrayFunction("some");
35        }
36        var xhrGet = function(){
37                return dojo.xhrGet.apply(dojo,arguments);
38        };
39        dojox.secure.sandbox = function(element) {
40                // summary:
41                //              Creates a secure sandbox from which scripts and HTML can be loaded that
42                //              will only be able to access the provided element and it's descendants, the
43                //              rest of the DOM and JS environment will not be accessible to the sandboxed
44                //              scripts and HTML.
45                //
46                // element:
47                //              The DOM element to use as the container for the sandbox
48                //
49                // description:
50                //              This function will create and return a sandbox object (see dojox.secure.__Sandbox)
51                //              for the provided element.
52                var wrap = dojox.secure.DOM(element);
53                element = wrap(element);
54                var document = element.ownerDocument;
55                var mixin, dojo = dojox.secure._safeDojoFunctions(element,wrap);
56                var imports= [];
57                var safeCalls = ["isNaN","isFinite","parseInt","parseFloat","escape","unescape",
58                                                                                "encodeURI","encodeURIComponent","decodeURI","decodeURIComponent",
59                                                                                "alert","confirm","prompt", // some people may not want to allow these to be called, but they don't break capability-limiting
60                                                                                "Error","EvalError","RangeError","ReferenceError","SyntaxError","TypeError",
61                                                                                "Date","RegExp","Number","Object","Array","String","Math",
62                                                                                //"ADSAFE", // not using ADSAFE runtime for the time being
63                                                                                "setTimeout","setInterval","clearTimeout","clearInterval", // we make these safe below
64                                                                                "dojo","get","set","forEach","load","evaluate"];
65                for(var i in dojo){
66                        safeCalls.push(i); // add the safe dojo functions to as available global top level functions
67                imports.push("var " + i + "=dojo." + i); // add to the list of imports
68            }
69                // open the dojo namespace (namespaces are pretty silly in an environment where you can't set globals)
70                eval(imports.join(";"));
71                function get(obj,prop) {
72                        // basic access by index function
73                        prop = '' + prop;
74                        if(dojox.secure.badProps.test(prop)) {
75                                throw new Error("bad property access");
76                        }
77                        if(obj.__get__) {
78                                return obj.__get__(prop);
79                        }
80                        return obj[prop];
81                }
82                function set(obj,prop,value) {
83                        // basic set by index function
84                        prop = '' + prop;
85                        get(obj,prop); // test it
86                        if(obj.__set) {
87                                return obj.__set(prop);
88                        }
89                        obj[prop] = value;
90                        return value;
91                }
92                function forEach(obj,fun) {
93                        //      short syntax iterator function
94                        if(typeof fun != "function"){
95                                throw new TypeError();
96                        }
97                        if("length" in obj) {
98                                // do arrays the fast way
99                                if(obj.__get__) {
100                                        // use the catch getter
101                                        var len = obj.__get__('length');
102                                        for (var i = 0; i < len; i++) {
103                                                if(i in obj) {
104                                                        fun.call(obj, obj.__get__(i), i, obj);
105                                                }
106                                        }
107                                }
108                                else {
109                                        // fast
110                                        len = obj.length;
111                                        for (i = 0; i < len; i++) {
112                                                if(i in obj) {
113                                                        fun.call(obj, obj[i], i, obj);
114                                                }
115                                        }
116                                }
117                        }
118                        else {
119                                // for each an object
120                                for (i in obj) {
121                                        fun.call(obj, get(obj,i), i, obj);
122                                }
123                        }
124                }
125                function Class(/*Function*/superclass, /*Object*/properties, /*Object*/classProperties) {
126                        // summary:
127                        //              A safe class constructor
128                        //
129                        // superclass:
130                        //              There may be zero or more superclass arguments. The constructed class
131                        //              will inherit from any provided superclasses, protypically from the first,
132                        //              via mixin for the subsequent. Later arguments
133                        //              will override properties/methods from earlier arguments
134                        //
135                        // properties:
136                        //              The constructed
137                        //              "class" will also have the methods/properties defined in this argument.
138                        //              These methods may utilize the <em>this</em> operator, and they
139                        //              are only the code that has access to <em>this</em>. Inner functions
140                        //              are also prohibited from using <em>this</em>.
141                        //
142                        //              If no superclasses are provided, this object will be the prototype of the
143                        //              constructed class (no copying
144                        //              will be done). Consequently you can "beget" by calling new (Class(obj)).
145                        //              All methods are "bound", each call results in |this| safety checking call.
146                        //
147                        // classProperties:
148                        //              This properties will be copied to the new class function.
149                        //
150                        //              Note that neither dojo.declare nor dojo.extend are acceptable class constructors as
151                        //              they are completely unsecure. This class constructor is conceptually based on declare
152                        //              but also somewhat influenced by base2, prototype, YUI, resig's patterns, etc.
153                        //
154                        // example:
155                        // |    var Car = Class({drive:function(speed) { ... } ); // create a Car class with a "drive" method
156                        // |    var FastCar = Class(Car,{driveFast: function(speed) { return this.drive(2 * speed); } }); // create a FastCar that extends Car
157                        // |    var fastCar = new FastCar; // instantiate
158                        // |    fastCar.driveFast(50); // call a method
159                        // |    var driveFast = fastCar.driveFast;
160                        // |    var driveFast(50); // this will throw an error, the method can be used with an object that is not an instance of FastCar
161                        var proto,superConstructor,ourConstructor;
162                        var arg;
163                        for (var i = 0, l = arguments.length; typeof (arg = arguments[i]) == 'function' && i < l; i++) {
164                                // go through each superclass argument
165                                if(proto) { // we have a prototype now, we must mixin now
166                                        mixin(proto,arg.prototype);
167                                }
168                                else {
169                                        // this is the first argument, so we can define the prototype ourselves
170                                        // link up the prototype chain to the superclass's prototype, so we are a subtype
171                                        superConstructor = arg;
172                                        var F = function() {};
173                                        F.prototype = arg.prototype;
174                                        proto = new F;
175                                }
176                        }
177
178                        if(arg) { // the next object should be the properties
179                                // apply binding checking on all the functions
180                                for (var j in arg) {
181                                        // TODO: check on non-enumerables?
182                                        var value = arg[j];
183                                        if(typeof value == 'function') {
184                                                arg[j] = function() {
185                                                        if(this instanceof Class){
186                                                                return arguments.callee.__rawMethod__.apply(this,arguments);
187                                                        }
188                                                        throw new Error("Method called on wrong object");
189                                                };
190                                                arg[j].__rawMethod__ = value; // may want to use this for reconstruction and toString,valueOf
191                                        }
192                                }
193                                if(arg.hasOwnProperty('constructor')) {
194                                        ourConstructor = arg.constructor;
195                                }
196                        }
197                        proto = proto ? mixin(proto,arg) : arg; // if there is no proto yet, we can use the provided object
198                        function Class() {
199                                // the super class may not have been constructed using the same technique, we will just call the constructor
200                                if(superConstructor){
201                                        superConstructor.apply(this,arguments);
202                                }
203                                if(ourConstructor){
204                                        ourConstructor.apply(this,arguments);
205                                }
206                        }
207                        mixin(Class,arguments[i]); // the optional second object adds properties to the class
208                        proto.constructor = Class;
209                        Class.prototype = proto;
210                        return Class;
211                }
212                function checkString(func){
213                        if(typeof func != 'function') {
214                                throw new Error("String is not allowed in setTimeout/setInterval");
215                        }
216                }
217                function setTimeout(func,time) {
218                        // sandboxed setTimeout
219                        checkString(func);
220                        return oldTimeout(func,time);
221                }
222                function setInterval(func,time) {
223                        // sandboxed setInterval
224                        checkString(func);
225                        return oldInterval(func,time);
226                }
227                function evaluate(script){
228                        // sandboxed eval
229                        return wrap.evaluate(script);
230                }
231                var load = wrap.load = function(url){
232                        // provides a loader function for the sandbox
233                        if (url.match(/^[\w\s]*:/)){
234                                throw new Error("Access denied to cross-site requests");
235                        }
236                        return xhrGet({url:(new dojo._Url(wrap.rootUrl,url))+'',secure:true});
237                }
238                wrap.evaluate = function(script){
239                        //if(!alreadyValidated) {
240                        dojox.secure.capability.validate(script,safeCalls, // the safe dojo library and standard operators
241                                                                                        {document:1,element:1}); // these are secured DOM starting points
242
243                        //}
244                        if(script.match(/^\s*[\[\{]/)) {
245                                var result = eval('(' + script + ')');
246                                // TODO: call render on result?
247                        }
248                        else {
249                                eval(script);
250                        }
251                        //eval('wrap.evaluate=('+arguments.callee.toString()+')'); // yeah, recursive scoping;
252                };
253                return /*===== dojo.declare("dojox.secure.__Sandbox", null, =====*/ { // dojox.secure.__Sandbox
254                        loadJS : function(url){
255                                // summary:
256                                //              Loads the script from the given URL using XHR (assuming
257                                //              a plugin system is in place for cross-site requests) within the sandbox
258                                //
259                                // url:
260                                //              The url of the script to load
261                                wrap.rootUrl = url;
262                                return xhrGet({url:url,secure:true}).addCallback(function(result) {
263                                        evaluate(result,element /*If we get the results with a secure proxy, we would call put true here */);
264                                });
265                        },
266                        loadHTML : function(url){
267                                // summary:
268                                //              Loads the web page from the provided URL using XHR (assuming the
269                                //              plugin system is in place) within the sandbox. All scripts within the web
270                                //              page will also be sandboxed.
271                                //
272                                // url:
273                                //              The url of the web page to load
274
275                                wrap.rootUrl = url;
276                                return xhrGet({url:url,secure:true}).addCallback(function(result){
277                                        element.innerHTML = result;
278                                });
279                        },
280                        evaluate : function(script){
281                                // summary:
282                                //              Evaluates the given script within the sandbox
283                                //
284                                // script:
285                                //              The JavaScript text to evaluate
286                                return wrap.evaluate(script);
287                        }
288                        // TODO: could add something for pre-validated scripts
289                }/*===== ) =====*/;
290        };
291})();
292dojox.secure._safeDojoFunctions = function(element,wrap) {
293        //      Creates a safe subset of Dojo core library
294        var safeFunctions = ["mixin","require","isString","isArray","isFunction","isObject","isArrayLike","isAlien",
295        "hitch","delegate","partial","trim","disconnect","subscribe","unsubscribe","Deferred","toJson","style","attr"];
296        //var domFunctions = ["clone","byId"];
297        var doc = element.ownerDocument;
298        var unwrap = dojox.secure.unwrap;
299        dojo.NodeList.prototype.addContent.safetyCheck = function(content){
300                wrap.safeHTML(content);
301        };
302        dojo.NodeList.prototype.style.safetyCheck = function(name,value){
303                if(name=='behavior'){
304                        throw new Error("Can not set behavior");
305                }
306                wrap.safeCSS(value);
307        };
308        dojo.NodeList.prototype.attr.safetyCheck = function(name,value){
309                if (value && (name == 'src' || name == 'href' || name=='style')){
310                        throw new Error("Illegal to set " + name);
311                }
312        };
313        var safe = {
314                query : function(query,root) {
315                        return wrap(dojo.query(query,unwrap(root || element))); // wrap the NodeList
316                },
317                connect: function(el,event) {
318                        var obj = el;
319                        arguments[0] = unwrap(el);
320                        if(obj!=arguments[0] && event.substring(0,2) != 'on'){
321                                // it is probably an element, and it doesn't look like an event handler, probably not safe
322                                throw new Error("Invalid event name for element");
323                        }
324                        return dojo.connect.apply(dojo,arguments);
325                },
326                body : function() {
327                        return element;
328                },
329                byId : function(id) {
330                        return element.ownerDocument.getElementById(id); // use the safe document
331                },
332                fromJson : function(str) {
333                        // make sure it is safe before passing it to the unsafe dojo.fromJson
334                        dojox.secure.capability.validate(str,[],{});
335                        return dojo.fromJson(str);
336                }
337        };
338        for (var i = 0; i < safeFunctions.length; i++) {
339                safe[safeFunctions[i]] = dojo[safeFunctions[i]];
340        }
341        return safe;
342};
343
Note: See TracBrowser for help on using the repository browser.