source: Dev/branches/rest-dojo-ui/client/dojox/secure/DOM.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: 7.6 KB
Line 
1dojo.provide("dojox.secure.DOM");
2dojo.require("dojox.lang.observable");
3
4dojox.secure.DOM = function(element){
5        function safeNode(node){
6                if(!node){
7                        return node;
8                }
9                var parent = node;
10                do {
11                        if(parent == element){
12                                return wrap(node);
13                        }
14                } while((parent = parent.parentNode));
15                return null;
16        }
17        function wrap(result){
18                if(result){
19                        if(result.nodeType){
20                                // wrap the node
21                                var wrapped = nodeObserver(result);
22                                if(result.nodeType == 1 && typeof wrapped.style == 'function'){ // if it is a function, that means it is holding a slot for us, now we will override it
23                                        wrapped.style = styleObserver(result.style);
24                                        wrapped.ownerDocument = safeDoc;
25                                        wrapped.childNodes = {__get__:function(i){
26                                                return wrap(result.childNodes[i]);
27                                                },
28                                                length:0
29                                        };
30                                        //TODO: maybe add attributes
31                                }
32                                return wrapped;
33                        }
34                        if(result && typeof result == 'object'){
35                                if(result.__observable){
36                                        // we have already wrapped it, this helps prevent circular/infinite loops
37                                        return result.__observable;
38                                }
39                                // wrap the node list
40                                wrapped = result instanceof Array ? [] : {};
41                                result.__observable = wrapped;
42                                for(var i in result){
43                                        if (i != '__observable'){
44                                                wrapped[i] = wrap(result[i]);
45                                        }
46                                }
47                                wrapped.data__ = result;
48                               
49                                return wrapped;
50                        }
51                        if(typeof result == 'function'){
52                                var unwrap = function(result){
53                                        if(typeof result == 'function'){
54                                                // if untrusted code passes a function to trusted code, we want the trusted code to be
55                                                // able to execute it and have the arguments automatically wrapped
56                                                return function(){
57                                                        for (var i = 0; i < arguments.length; i++){
58                                                                arguments[i] = wrap(arguments[i]);
59                                                        }
60                                                        return unwrap(result.apply(wrap(this),arguments));
61                                                }
62                                        }
63                                        return dojox.secure.unwrap(result);
64                                };
65                                // when we wrap a function we make it so that we can untrusted code can execute
66                                // the function and the arguments will be unwrapped for the trusted code
67                                return function(){
68                                        if(result.safetyCheck){
69                                                result.safetyCheck.apply(unwrap(this),arguments);
70                                        }
71                                        for (var i = 0; i < arguments.length; i++){
72                                                arguments[i] = unwrap(arguments[i]);
73                                        }
74                                        return wrap(result.apply(unwrap(this),arguments));
75                                }
76                        }
77                }
78                return result;
79        }
80        unwrap = dojox.secure.unwrap;
81       
82        function safeCSS(css){
83                css += ''; // make sure it is a string
84                if(css.match(/behavior:|content:|javascript:|binding|expression|\@import/)){
85                        throw new Error("Illegal CSS");
86                }
87                var id = element.id || (element.id = "safe" + ('' + Math.random()).substring(2));
88                return css.replace(/(\}|^)\s*([^\{]*\{)/g,function(t,a,b){ // put all the styles in the context of the id of the sandbox
89                        return a + ' #' + id + ' ' + b; // need to remove body and html references something like: .replace(/body/g,''); but that would break mybody...
90                });
91        }
92        function safeURL(url){
93                // test a url to see if it is safe
94                if(url.match(/:/) && !url.match(/^(http|ftp|mailto)/)){
95                        throw new Error("Unsafe URL " + url);
96                }
97        }
98        function safeElement(el){
99                // test an element to see if it is safe
100                if(el && el.nodeType == 1){
101                        if(el.tagName.match(/script/i)){
102                                var src = el.src;
103                                if (src && src != ""){
104                                        // load the src and evaluate it safely
105                                        el.parentNode.removeChild(el);
106                                        dojo.xhrGet({url:src,secure:true}).addCallback(function(result){
107                                                safeDoc.evaluate(result);
108                                        });
109                                }
110                                else{
111                                        //evaluate the script safely and remove it
112                                        var script = el.innerHTML;
113                                        el.parentNode.removeChild(el);
114                                        wrap.evaluate(script);
115                                }
116                        }
117                        if(el.tagName.match(/link/i)){
118                                throw new Error("illegal tag");
119                        }
120                        if(el.tagName.match(/style/i)){
121                                var setCSS = function(cssStr){
122                                        if(el.styleSheet){// IE
123                                                el.styleSheet.cssText = cssStr;
124                                        } else {// w3c
125                                                var cssText = doc.createTextNode(cssStr);
126                                                if (el.childNodes[0])
127                                                        el.replaceChild(cssText,el.childNodes[0])
128                                                else
129                                                        el.appendChild(cssText);
130                                         }
131                                       
132                                }
133                                src = el.src;
134                                if(src && src != ""){
135                                        alert('src' + src);
136                                        // try to load it by url and safely load it
137                                        el.src = null;
138                                        dojo.xhrGet({url:src,secure:true}).addCallback(function(result){
139                                                setCSS(safeCSS(result));
140                                        });
141                                }
142                                setCSS(safeCSS(el.innerHTML));
143                        }
144                        if(el.style){
145                                safeCSS(el.style.cssText);
146                        }
147                        if(el.href){
148                                safeURL(el.href);
149                        }
150                        if(el.src){
151                                safeURL(el.src);
152                        }
153                        var attr,i = 0;
154                        while ((attr=el.attributes[i++])){
155                                if(attr.name.substring(0,2)== "on" && attr.value != "null" && attr.value != ""){ // must remove all the event handlers
156                                        throw new Error("event handlers not allowed in the HTML, they must be set with element.addEventListener");
157                                }
158                        }
159                        var children = el.childNodes;
160                        for (var i =0, l = children.length; i < l; i++){
161                                safeElement(children[i]);
162                        }
163                }
164        }
165        function safeHTML(html){
166                var div = document.createElement("div");
167                if(html.match(/<object/i))
168                        throw new Error("The object tag is not allowed");
169                div.innerHTML = html; // this is safe with an unattached node
170                safeElement(div);
171                return div;
172        }
173        var doc = element.ownerDocument;
174        var safeDoc = {
175                getElementById : function(id){
176                        return safeNode(doc.getElementById(id));
177                },
178                createElement : function(name){
179                        return wrap(doc.createElement(name));
180                },
181                createTextNode : function(name){
182                        return wrap(doc.createTextNode(name));
183                },
184                write : function(str){
185                        var div = safeHTML(str);
186                        while (div.childNodes.length){
187                                // move all these children to the main node
188                                element.appendChild(div.childNodes[0]);
189                        }
190                }
191        };
192        safeDoc.open = safeDoc.close = function(){}; // no-op functions
193        var setters = {
194                innerHTML : function(node,value){
195                        console.log('setting innerHTML');
196                        node.innerHTML = safeHTML(value).innerHTML;
197                }
198        };
199        setters.outerHTML = function(node,value){
200                throw new Error("Can not set this property");
201        }; // blocked
202        function domChanger(name,newNodeArg){
203                return function(node,args){
204                        safeElement(args[newNodeArg]);  // check to make sure the new node is safe
205                        return node[name](args[0]);// execute the method
206                };
207        }
208        var invokers = {
209                appendChild : domChanger("appendChild",0),
210                insertBefore : domChanger("insertBefore",0),
211                replaceChild : domChanger("replaceChild",1),
212                cloneNode : function(node,args){
213                        return node.cloneNode(args[0]);
214                },
215                addEventListener : function(node,args){
216                        dojo.connect(node,'on' + args[0],this,function(event){
217                                event = nodeObserver(event || window.event);
218                                args[1].call(this,event);
219                        });
220                }
221        };
222        invokers.childNodes = invokers.style = invokers.ownerDocument = function(){}; // this is a trick to get these property slots available, they will be overridden
223        function makeObserver(setter){ // we make two of these, but the setter for style nodes is different
224                return dojox.lang.makeObservable(
225                        function(node, prop){
226                                var result;
227                                return node[prop];
228                        },setter,
229                        function(wrapper, node, methodName, args){
230                                for (var i = 0; i < args.length; i++){
231                                        args[i] = unwrap(args[i]);
232                                }
233                                if(invokers[methodName]){
234                                        return wrap(invokers[methodName].call(wrapper,node,args));
235                                }
236                                return wrap(node[methodName].apply(node,args));
237                        },invokers);
238        }
239        var nodeObserver = makeObserver(function(node, prop, value){
240                        if(setters[prop]){
241                                setters[prop](node,value);
242                        }
243                        node[prop] = value;
244                });
245        var blockedStyles = {behavior:1,MozBinding:1};
246        var styleObserver = makeObserver(function(node, prop, value){
247                        if(!blockedStyles[prop]){
248                                node[prop] = safeCSS(value);
249                        }
250                });
251        wrap.safeHTML = safeHTML;
252        wrap.safeCSS = safeCSS;
253        return wrap;
254};
255dojox.secure.unwrap = function unwrap(result){
256        return (result && result.data__) || result;
257};
Note: See TracBrowser for help on using the repository browser.