source: Dev/trunk/src/client/dojox/secure/DOM.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: 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.