safe_test.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. // Copyright 2013 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Unit tests for goog.dom.safe.
  16. */
  17. goog.provide('goog.dom.safeTest');
  18. goog.setTestOnly('goog.dom.safeTest');
  19. goog.require('goog.dom.safe');
  20. goog.require('goog.dom.safe.InsertAdjacentHtmlPosition');
  21. goog.require('goog.html.SafeHtml');
  22. goog.require('goog.html.SafeScript');
  23. goog.require('goog.html.SafeStyle');
  24. goog.require('goog.html.SafeUrl');
  25. goog.require('goog.html.TrustedResourceUrl');
  26. goog.require('goog.html.testing');
  27. goog.require('goog.string');
  28. goog.require('goog.string.Const');
  29. goog.require('goog.testing');
  30. goog.require('goog.testing.jsunit');
  31. goog.require('goog.userAgent');
  32. var mockWindowOpen;
  33. function tearDown() {
  34. if (mockWindowOpen) {
  35. mockWindowOpen.$tearDown();
  36. }
  37. }
  38. function testInsertAdjacentHtml() {
  39. var writtenHtml;
  40. var writtenPosition;
  41. var mockNode = /** @type {!Node} */ ({
  42. 'insertAdjacentHTML': function(position, html) {
  43. writtenPosition = position;
  44. writtenHtml = html;
  45. }
  46. });
  47. goog.dom.safe.insertAdjacentHtml(
  48. mockNode, goog.dom.safe.InsertAdjacentHtmlPosition.BEFOREBEGIN,
  49. goog.html.SafeHtml.create('div', {}, 'foobar'));
  50. assertEquals('<div>foobar</div>', writtenHtml);
  51. assertEquals('beforebegin', writtenPosition);
  52. }
  53. function testSetInnerHtml() {
  54. var mockElement =
  55. /** @type {!Element} */ ({'tagName': 'DIV', 'innerHTML': 'blarg'});
  56. var html = '<script>somethingTrusted();<' +
  57. '/script>';
  58. var safeHtml = goog.html.testing.newSafeHtmlForTest(html);
  59. goog.dom.safe.setInnerHtml(mockElement, safeHtml);
  60. assertEquals(html, mockElement.innerHTML);
  61. }
  62. function testSetInnerHtml_doesntAllowScript() {
  63. var script =
  64. /** @type {!Element} */ ({'tagName': 'SCRIPT', 'innerHTML': 'blarg'});
  65. var safeHtml = goog.html.SafeHtml.htmlEscape('alert(1);');
  66. assertThrows(function() { goog.dom.safe.setInnerHtml(script, safeHtml); });
  67. }
  68. function testSetInnerHtml_doesntAllowStyle() {
  69. var style =
  70. /** @type {!Element} */ ({'tagName': 'STYLE', 'innerHTML': 'blarg'});
  71. var safeHtml = goog.html.SafeHtml.htmlEscape('A { color: red; }');
  72. assertThrows(function() { goog.dom.safe.setInnerHtml(style, safeHtml); });
  73. }
  74. function testSetStyle() {
  75. var style =
  76. goog.html.SafeStyle.fromConstant(goog.string.Const.from('color: red;'));
  77. var elem = document.createElement('div');
  78. assertEquals('', elem.style.color); // sanity check
  79. goog.dom.safe.setStyle(elem, style);
  80. assertEquals('red', elem.style.color);
  81. }
  82. function testDocumentWrite() {
  83. var mockDoc = /** @type {!Document} */ ({
  84. 'html': null,
  85. /** @suppress {globalThis} */
  86. 'write': function(html) { this['html'] = html; }
  87. });
  88. var html = '<script>somethingTrusted();<' +
  89. '/script>';
  90. var safeHtml = goog.html.testing.newSafeHtmlForTest(html);
  91. goog.dom.safe.documentWrite(mockDoc, safeHtml);
  92. assertEquals(html, mockDoc.html);
  93. }
  94. function testsetLinkHrefAndRel_trustedResourceUrl() {
  95. var mockLink = /** @type {!HTMLLinkElement} */ ({'href': null, 'rel': null});
  96. var url = goog.html.TrustedResourceUrl.fromConstant(
  97. goog.string.Const.from('javascript:trusted();'));
  98. // Test case-insensitive too.
  99. goog.dom.safe.setLinkHrefAndRel(mockLink, url, 'foo, Stylesheet, bar');
  100. assertEquals('javascript:trusted();', mockLink.href);
  101. goog.dom.safe.setLinkHrefAndRel(mockLink, url, 'foo, bar');
  102. assertEquals('javascript:trusted();', mockLink.href);
  103. }
  104. function testsetLinkHrefAndRel_safeUrl() {
  105. var mockLink = /** @type {!HTMLLinkElement} */ ({'href': null, 'rel': null});
  106. var url = goog.html.SafeUrl.fromConstant(
  107. goog.string.Const.from('javascript:trusted();'));
  108. assertThrows(function() {
  109. goog.dom.safe.setLinkHrefAndRel(mockLink, url, 'foo, stylesheet, bar');
  110. });
  111. goog.dom.safe.setLinkHrefAndRel(mockLink, url, 'foo, bar');
  112. assertEquals('javascript:trusted();', mockLink.href);
  113. }
  114. function testsetLinkHrefAndRel_string() {
  115. var mockLink = /** @type {!HTMLLinkElement} */ ({'href': null, 'rel': null});
  116. assertThrows(function() {
  117. goog.dom.safe.setLinkHrefAndRel(
  118. mockLink, 'javascript:evil();', 'foo, stylesheet, bar');
  119. });
  120. goog.dom.safe.setLinkHrefAndRel(mockLink, 'javascript:evil();', 'foo, bar');
  121. assertEquals('about:invalid#zClosurez', mockLink.href);
  122. }
  123. function testsetLinkHrefAndRel_assertsType() {
  124. if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
  125. var otherElement = document.createElement('A');
  126. var ex = assertThrows(function() {
  127. goog.dom.safe.setLinkHrefAndRel(
  128. /** @type {!HTMLLinkElement} */ (otherElement), 'http://example.com/',
  129. 'author');
  130. });
  131. assert(
  132. goog.string.contains(ex.message, 'Argument is not a HTMLLinkElement'));
  133. }
  134. }
  135. /**
  136. * Returns a link element, incorrectly typed as a Location.
  137. * @return {!Location}
  138. * @suppress {checkTypes}
  139. */
  140. function makeLinkElementTypedAsLocation() {
  141. return document.createElement('LINK');
  142. }
  143. function testSetLocationHref() {
  144. var mockLoc = /** @type {!Location} */ ({'href': 'blarg'});
  145. goog.dom.safe.setLocationHref(mockLoc, 'javascript:evil();');
  146. assertEquals('about:invalid#zClosurez', mockLoc.href);
  147. mockLoc = /** @type {!Location} */ ({'href': 'blarg'});
  148. var safeUrl = goog.html.SafeUrl.fromConstant(
  149. goog.string.Const.from('javascript:trusted();'));
  150. goog.dom.safe.setLocationHref(mockLoc, safeUrl);
  151. assertEquals('javascript:trusted();', mockLoc.href);
  152. // Asserts correct runtime type.
  153. if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
  154. var ex = assertThrows(function() {
  155. goog.dom.safe.setLocationHref(makeLinkElementTypedAsLocation(), safeUrl);
  156. });
  157. assert(goog.string.contains(ex.message, 'Argument is not a Location'));
  158. }
  159. }
  160. function testSetAnchorHref() {
  161. var anchor = /** @type {!HTMLAnchorElement} */ (document.createElement('A'));
  162. goog.dom.safe.setAnchorHref(anchor, 'javascript:evil();');
  163. assertEquals('about:invalid#zClosurez', anchor.href);
  164. anchor = /** @type {!HTMLAnchorElement} */ (document.createElement('A'));
  165. var safeUrl = goog.html.SafeUrl.fromConstant(
  166. goog.string.Const.from('javascript:trusted();'));
  167. goog.dom.safe.setAnchorHref(anchor, safeUrl);
  168. assertEquals('javascript:trusted();', anchor.href);
  169. // Works with mocks too.
  170. var mockAnchor = /** @type {!HTMLAnchorElement} */ ({'href': 'blarg'});
  171. goog.dom.safe.setAnchorHref(mockAnchor, 'javascript:evil();');
  172. assertEquals('about:invalid#zClosurez', mockAnchor.href);
  173. mockAnchor = /** @type {!HTMLAnchorElement} */ ({'href': 'blarg'});
  174. safeUrl = goog.html.SafeUrl.fromConstant(
  175. goog.string.Const.from('javascript:trusted();'));
  176. goog.dom.safe.setAnchorHref(mockAnchor, safeUrl);
  177. assertEquals('javascript:trusted();', mockAnchor.href);
  178. // Asserts correct runtime type.
  179. if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
  180. var otherElement = document.createElement('LINK');
  181. var ex = assertThrows(function() {
  182. goog.dom.safe.setAnchorHref(
  183. /** @type {!HTMLAnchorElement} */ (otherElement), safeUrl);
  184. });
  185. assert(goog.string.contains(
  186. ex.message, 'Argument is not a HTMLAnchorElement'));
  187. }
  188. }
  189. function testSetImageSrc_withSafeUrlObject() {
  190. var mockImageElement = /** @type {!HTMLImageElement} */ ({'src': 'blarg'});
  191. goog.dom.safe.setImageSrc(mockImageElement, 'javascript:evil();');
  192. assertEquals('about:invalid#zClosurez', mockImageElement.src);
  193. mockImageElement = /** @type {!HTMLImageElement} */ ({'src': 'blarg'});
  194. var safeUrl = goog.html.SafeUrl.fromConstant(
  195. goog.string.Const.from('javascript:trusted();'));
  196. goog.dom.safe.setImageSrc(mockImageElement, safeUrl);
  197. assertEquals('javascript:trusted();', mockImageElement.src);
  198. // Asserts correct runtime type.
  199. if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
  200. var otherElement = document.createElement('SCRIPT');
  201. var ex = assertThrows(function() {
  202. goog.dom.safe.setImageSrc(
  203. /** @type {!HTMLImageElement} */ (otherElement), safeUrl);
  204. });
  205. assert(
  206. goog.string.contains(ex.message, 'Argument is not a HTMLImageElement'));
  207. }
  208. }
  209. function testSetImageSrc_withHttpsUrl() {
  210. var mockImageElement = /** @type {!HTMLImageElement} */ ({'src': 'blarg'});
  211. var safeUrl = 'https://trusted_url';
  212. goog.dom.safe.setImageSrc(mockImageElement, safeUrl);
  213. assertEquals(safeUrl, mockImageElement.src);
  214. }
  215. function testSetEmbedSrc() {
  216. var url = goog.html.TrustedResourceUrl.fromConstant(
  217. goog.string.Const.from('javascript:trusted();'));
  218. var mockElement = /** @type {!HTMLEmbedElement} */ ({'src': 'blarg'});
  219. goog.dom.safe.setEmbedSrc(mockElement, url);
  220. assertEquals('javascript:trusted();', mockElement.src);
  221. // Asserts correct runtime type.
  222. if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
  223. var otherElement = document.createElement('IMAGE');
  224. var ex = assertThrows(function() {
  225. goog.dom.safe.setEmbedSrc(
  226. /** @type {!HTMLEmbedElement} */ (otherElement), url);
  227. });
  228. assert(
  229. goog.string.contains(ex.message, 'Argument is not a HTMLEmbedElement'));
  230. }
  231. }
  232. function testSetFrameSrc() {
  233. var url = goog.html.TrustedResourceUrl.fromConstant(
  234. goog.string.Const.from('javascript:trusted();'));
  235. var mockElement = /** @type {!HTMLFrameElement} */ ({'src': 'blarg'});
  236. goog.dom.safe.setFrameSrc(mockElement, url);
  237. assertEquals('javascript:trusted();', mockElement.src);
  238. // Asserts correct runtime type.
  239. if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
  240. var otherElement = document.createElement('IMAGE');
  241. var ex = assertThrows(function() {
  242. goog.dom.safe.setFrameSrc(
  243. /** @type {!HTMLFrameElement} */ (otherElement), url);
  244. });
  245. assert(
  246. goog.string.contains(ex.message, 'Argument is not a HTMLFrameElement'));
  247. }
  248. }
  249. function testSetIframeSrc() {
  250. var url = goog.html.TrustedResourceUrl.fromConstant(
  251. goog.string.Const.from('javascript:trusted();'));
  252. var mockElement = /** @type {!HTMLIFrameElement} */ ({'src': 'blarg'});
  253. goog.dom.safe.setIframeSrc(mockElement, url);
  254. assertEquals('javascript:trusted();', mockElement.src);
  255. // Asserts correct runtime type.
  256. if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
  257. var otherElement = document.createElement('IMAGE');
  258. var ex = assertThrows(function() {
  259. goog.dom.safe.setIframeSrc(
  260. /** @type {!HTMLIFrameElement} */ (otherElement), url);
  261. });
  262. assert(goog.string.contains(
  263. ex.message, 'Argument is not a HTMLIFrameElement'));
  264. }
  265. }
  266. function testSetIframeSrcdoc() {
  267. var html = goog.html.SafeHtml.create('div', {}, 'foobar');
  268. var mockIframe = /** @type {!HTMLIFrameElement} */ ({'srcdoc': ''});
  269. goog.dom.safe.setIframeSrcdoc(mockIframe, html);
  270. assertEquals('<div>foobar</div>', mockIframe.srcdoc);
  271. // Asserts correct runtime type.
  272. if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
  273. var otherElement = document.createElement('IMAGE');
  274. var ex = assertThrows(function() {
  275. goog.dom.safe.setIframeSrcdoc(
  276. /** @type {!HTMLIFrameElement} */ (otherElement), html);
  277. });
  278. assert(goog.string.contains(
  279. ex.message, 'Argument is not a HTMLIFrameElement'));
  280. }
  281. }
  282. function testSetObjectData() {
  283. var url = goog.html.TrustedResourceUrl.fromConstant(
  284. goog.string.Const.from('javascript:trusted();'));
  285. var mockElement = /** @type {!HTMLObjectElement} */ ({'data': 'blarg'});
  286. goog.dom.safe.setObjectData(mockElement, url);
  287. assertEquals('javascript:trusted();', mockElement.data);
  288. // Asserts correct runtime type.
  289. if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
  290. var otherElement = document.createElement('IMAGE');
  291. var ex = assertThrows(function() {
  292. goog.dom.safe.setObjectData(
  293. /** @type {!HTMLObjectElement} */ (otherElement), url);
  294. });
  295. assert(goog.string.contains(
  296. ex.message, 'Argument is not a HTMLObjectElement'));
  297. }
  298. }
  299. function testSetScriptSrc() {
  300. var url = goog.html.TrustedResourceUrl.fromConstant(
  301. goog.string.Const.from('javascript:trusted();'));
  302. var mockElement = /** @type {!HTMLScriptElement} */ ({'src': 'blarg'});
  303. goog.dom.safe.setScriptSrc(mockElement, url);
  304. assertEquals('javascript:trusted();', mockElement.src);
  305. // Asserts correct runtime type.
  306. if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
  307. var otherElement = document.createElement('IMAGE');
  308. var ex = assertThrows(function() {
  309. goog.dom.safe.setScriptSrc(
  310. /** @type {!HTMLScriptElement} */ (otherElement), url);
  311. });
  312. assert(goog.string.contains(
  313. ex.message, 'Argument is not a HTMLScriptElement'));
  314. }
  315. }
  316. function testSetScriptContent() {
  317. var mockScriptElement = /** @type {!HTMLScriptElement} */ ({});
  318. var content =
  319. goog.html.SafeScript.fromConstant(goog.string.Const.from('alert(1);'));
  320. goog.dom.safe.setScriptContent(mockScriptElement, content);
  321. assertEquals(goog.html.SafeScript.unwrap(content), mockScriptElement.text);
  322. }
  323. function testOpenInWindow() {
  324. mockWindowOpen =
  325. /** @type {?} */ (goog.testing.createMethodMock(window, 'open'));
  326. var fakeWindow = {};
  327. mockWindowOpen('about:invalid#zClosurez', 'name', 'specs', true)
  328. .$returns(fakeWindow);
  329. mockWindowOpen.$replay();
  330. var retVal = goog.dom.safe.openInWindow(
  331. 'javascript:evil();', window, goog.string.Const.from('name'), 'specs',
  332. true);
  333. mockWindowOpen.$verify();
  334. assertEquals(
  335. 'openInWindow should return the created window', fakeWindow, retVal);
  336. mockWindowOpen.$reset();
  337. retVal = null;
  338. var safeUrl = goog.html.SafeUrl.fromConstant(
  339. goog.string.Const.from('javascript:trusted();'));
  340. mockWindowOpen('javascript:trusted();', 'name', 'specs', true)
  341. .$returns(fakeWindow);
  342. mockWindowOpen.$replay();
  343. retVal = goog.dom.safe.openInWindow(
  344. safeUrl, window, goog.string.Const.from('name'), 'specs', true);
  345. mockWindowOpen.$verify();
  346. assertEquals(
  347. 'openInWindow should return the created window', fakeWindow, retVal);
  348. }