DOM.js 7.7 KB

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