source: Dev/branches/rest-dojo-ui/client/dojox/secure/sandbox.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 11.9 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.