safe.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  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 Type-safe wrappers for unsafe DOM APIs.
  16. *
  17. * This file provides type-safe wrappers for DOM APIs that can result in
  18. * cross-site scripting (XSS) vulnerabilities, if the API is supplied with
  19. * untrusted (attacker-controlled) input. Instead of plain strings, the type
  20. * safe wrappers consume values of types from the goog.html package whose
  21. * contract promises that values are safe to use in the corresponding context.
  22. *
  23. * Hence, a program that exclusively uses the wrappers in this file (i.e., whose
  24. * only reference to security-sensitive raw DOM APIs are in this file) is
  25. * guaranteed to be free of XSS due to incorrect use of such DOM APIs (modulo
  26. * correctness of code that produces values of the respective goog.html types,
  27. * and absent code that violates type safety).
  28. *
  29. * For example, assigning to an element's .innerHTML property a string that is
  30. * derived (even partially) from untrusted input typically results in an XSS
  31. * vulnerability. The type-safe wrapper goog.dom.safe.setInnerHtml consumes a
  32. * value of type goog.html.SafeHtml, whose contract states that using its values
  33. * in a HTML context will not result in XSS. Hence a program that is free of
  34. * direct assignments to any element's innerHTML property (with the exception of
  35. * the assignment to .innerHTML in this file) is guaranteed to be free of XSS
  36. * due to assignment of untrusted strings to the innerHTML property.
  37. */
  38. goog.provide('goog.dom.safe');
  39. goog.provide('goog.dom.safe.InsertAdjacentHtmlPosition');
  40. goog.require('goog.asserts');
  41. goog.require('goog.dom.asserts');
  42. goog.require('goog.html.SafeHtml');
  43. goog.require('goog.html.SafeScript');
  44. goog.require('goog.html.SafeStyle');
  45. goog.require('goog.html.SafeUrl');
  46. goog.require('goog.html.TrustedResourceUrl');
  47. goog.require('goog.string');
  48. goog.require('goog.string.Const');
  49. /** @enum {string} */
  50. goog.dom.safe.InsertAdjacentHtmlPosition = {
  51. AFTERBEGIN: 'afterbegin',
  52. AFTEREND: 'afterend',
  53. BEFOREBEGIN: 'beforebegin',
  54. BEFOREEND: 'beforeend'
  55. };
  56. /**
  57. * Inserts known-safe HTML into a Node, at the specified position.
  58. * @param {!Node} node The node on which to call insertAdjacentHTML.
  59. * @param {!goog.dom.safe.InsertAdjacentHtmlPosition} position Position where
  60. * to insert the HTML.
  61. * @param {!goog.html.SafeHtml} html The known-safe HTML to insert.
  62. */
  63. goog.dom.safe.insertAdjacentHtml = function(node, position, html) {
  64. node.insertAdjacentHTML(position, goog.html.SafeHtml.unwrap(html));
  65. };
  66. /**
  67. * Tags not allowed in goog.dom.safe.setInnerHtml.
  68. * @private @const {!Object<string, boolean>}
  69. */
  70. goog.dom.safe.SET_INNER_HTML_DISALLOWED_TAGS_ = {
  71. 'MATH': true,
  72. 'SCRIPT': true,
  73. 'STYLE': true,
  74. 'SVG': true,
  75. 'TEMPLATE': true
  76. };
  77. /**
  78. * Assigns known-safe HTML to an element's innerHTML property.
  79. * @param {!Element} elem The element whose innerHTML is to be assigned to.
  80. * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
  81. * @throws {Error} If called with one of these tags: math, script, style, svg,
  82. * template.
  83. */
  84. goog.dom.safe.setInnerHtml = function(elem, html) {
  85. if (goog.asserts.ENABLE_ASSERTS) {
  86. var tagName = elem.tagName.toUpperCase();
  87. if (goog.dom.safe.SET_INNER_HTML_DISALLOWED_TAGS_[tagName]) {
  88. throw Error(
  89. 'goog.dom.safe.setInnerHtml cannot be used to set content of ' +
  90. elem.tagName + '.');
  91. }
  92. }
  93. elem.innerHTML = goog.html.SafeHtml.unwrap(html);
  94. };
  95. /**
  96. * Assigns known-safe HTML to an element's outerHTML property.
  97. * @param {!Element} elem The element whose outerHTML is to be assigned to.
  98. * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
  99. */
  100. goog.dom.safe.setOuterHtml = function(elem, html) {
  101. elem.outerHTML = goog.html.SafeHtml.unwrap(html);
  102. };
  103. /**
  104. * Sets the given element's style property to the contents of the provided
  105. * SafeStyle object.
  106. * @param {!Element} elem
  107. * @param {!goog.html.SafeStyle} style
  108. */
  109. goog.dom.safe.setStyle = function(elem, style) {
  110. elem.style.cssText = goog.html.SafeStyle.unwrap(style);
  111. };
  112. /**
  113. * Writes known-safe HTML to a document.
  114. * @param {!Document} doc The document to be written to.
  115. * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
  116. */
  117. goog.dom.safe.documentWrite = function(doc, html) {
  118. doc.write(goog.html.SafeHtml.unwrap(html));
  119. };
  120. /**
  121. * Safely assigns a URL to an anchor element's href property.
  122. *
  123. * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
  124. * anchor's href property. If url is of type string however, it is first
  125. * sanitized using goog.html.SafeUrl.sanitize.
  126. *
  127. * Example usage:
  128. * goog.dom.safe.setAnchorHref(anchorEl, url);
  129. * which is a safe alternative to
  130. * anchorEl.href = url;
  131. * The latter can result in XSS vulnerabilities if url is a
  132. * user-/attacker-controlled value.
  133. *
  134. * @param {!HTMLAnchorElement} anchor The anchor element whose href property
  135. * is to be assigned to.
  136. * @param {string|!goog.html.SafeUrl} url The URL to assign.
  137. * @see goog.html.SafeUrl#sanitize
  138. */
  139. goog.dom.safe.setAnchorHref = function(anchor, url) {
  140. goog.dom.asserts.assertIsHTMLAnchorElement(anchor);
  141. /** @type {!goog.html.SafeUrl} */
  142. var safeUrl;
  143. if (url instanceof goog.html.SafeUrl) {
  144. safeUrl = url;
  145. } else {
  146. safeUrl = goog.html.SafeUrl.sanitize(url);
  147. }
  148. anchor.href = goog.html.SafeUrl.unwrap(safeUrl);
  149. };
  150. /**
  151. * Safely assigns a URL to an image element's src property.
  152. *
  153. * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
  154. * image's src property. If url is of type string however, it is first
  155. * sanitized using goog.html.SafeUrl.sanitize.
  156. *
  157. * @param {!HTMLImageElement} imageElement The image element whose src property
  158. * is to be assigned to.
  159. * @param {string|!goog.html.SafeUrl} url The URL to assign.
  160. * @see goog.html.SafeUrl#sanitize
  161. */
  162. goog.dom.safe.setImageSrc = function(imageElement, url) {
  163. goog.dom.asserts.assertIsHTMLImageElement(imageElement);
  164. /** @type {!goog.html.SafeUrl} */
  165. var safeUrl;
  166. if (url instanceof goog.html.SafeUrl) {
  167. safeUrl = url;
  168. } else {
  169. safeUrl = goog.html.SafeUrl.sanitize(url);
  170. }
  171. imageElement.src = goog.html.SafeUrl.unwrap(safeUrl);
  172. };
  173. /**
  174. * Safely assigns a URL to an embed element's src property.
  175. *
  176. * Example usage:
  177. * goog.dom.safe.setEmbedSrc(embedEl, url);
  178. * which is a safe alternative to
  179. * embedEl.src = url;
  180. * The latter can result in loading untrusted code unless it is ensured that
  181. * the URL refers to a trustworthy resource.
  182. *
  183. * @param {!HTMLEmbedElement} embed The embed element whose src property
  184. * is to be assigned to.
  185. * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
  186. */
  187. goog.dom.safe.setEmbedSrc = function(embed, url) {
  188. goog.dom.asserts.assertIsHTMLEmbedElement(embed);
  189. embed.src = goog.html.TrustedResourceUrl.unwrap(url);
  190. };
  191. /**
  192. * Safely assigns a URL to a frame element's src property.
  193. *
  194. * Example usage:
  195. * goog.dom.safe.setFrameSrc(frameEl, url);
  196. * which is a safe alternative to
  197. * frameEl.src = url;
  198. * The latter can result in loading untrusted code unless it is ensured that
  199. * the URL refers to a trustworthy resource.
  200. *
  201. * @param {!HTMLFrameElement} frame The frame element whose src property
  202. * is to be assigned to.
  203. * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
  204. */
  205. goog.dom.safe.setFrameSrc = function(frame, url) {
  206. goog.dom.asserts.assertIsHTMLFrameElement(frame);
  207. frame.src = goog.html.TrustedResourceUrl.unwrap(url);
  208. };
  209. /**
  210. * Safely assigns a URL to an iframe element's src property.
  211. *
  212. * Example usage:
  213. * goog.dom.safe.setIframeSrc(iframeEl, url);
  214. * which is a safe alternative to
  215. * iframeEl.src = url;
  216. * The latter can result in loading untrusted code unless it is ensured that
  217. * the URL refers to a trustworthy resource.
  218. *
  219. * @param {!HTMLIFrameElement} iframe The iframe element whose src property
  220. * is to be assigned to.
  221. * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
  222. */
  223. goog.dom.safe.setIframeSrc = function(iframe, url) {
  224. goog.dom.asserts.assertIsHTMLIFrameElement(iframe);
  225. iframe.src = goog.html.TrustedResourceUrl.unwrap(url);
  226. };
  227. /**
  228. * Safely assigns HTML to an iframe element's srcdoc property.
  229. *
  230. * Example usage:
  231. * goog.dom.safe.setIframeSrcdoc(iframeEl, safeHtml);
  232. * which is a safe alternative to
  233. * iframeEl.srcdoc = html;
  234. * The latter can result in loading untrusted code.
  235. *
  236. * @param {!HTMLIFrameElement} iframe The iframe element whose srcdoc property
  237. * is to be assigned to.
  238. * @param {!goog.html.SafeHtml} html The HTML to assign.
  239. */
  240. goog.dom.safe.setIframeSrcdoc = function(iframe, html) {
  241. goog.dom.asserts.assertIsHTMLIFrameElement(iframe);
  242. iframe.srcdoc = goog.html.SafeHtml.unwrap(html);
  243. };
  244. /**
  245. * Safely sets a link element's href and rel properties. Whether or not
  246. * the URL assigned to href has to be a goog.html.TrustedResourceUrl
  247. * depends on the value of the rel property. If rel contains "stylesheet"
  248. * then a TrustedResourceUrl is required.
  249. *
  250. * Example usage:
  251. * goog.dom.safe.setLinkHrefAndRel(linkEl, url, 'stylesheet');
  252. * which is a safe alternative to
  253. * linkEl.rel = 'stylesheet';
  254. * linkEl.href = url;
  255. * The latter can result in loading untrusted code unless it is ensured that
  256. * the URL refers to a trustworthy resource.
  257. *
  258. * @param {!HTMLLinkElement} link The link element whose href property
  259. * is to be assigned to.
  260. * @param {string|!goog.html.SafeUrl|!goog.html.TrustedResourceUrl} url The URL
  261. * to assign to the href property. Must be a TrustedResourceUrl if the
  262. * value assigned to rel contains "stylesheet". A string value is
  263. * sanitized with goog.html.SafeUrl.sanitize.
  264. * @param {string} rel The value to assign to the rel property.
  265. * @throws {Error} if rel contains "stylesheet" and url is not a
  266. * TrustedResourceUrl
  267. * @see goog.html.SafeUrl#sanitize
  268. */
  269. goog.dom.safe.setLinkHrefAndRel = function(link, url, rel) {
  270. goog.dom.asserts.assertIsHTMLLinkElement(link);
  271. link.rel = rel;
  272. if (goog.string.caseInsensitiveContains(rel, 'stylesheet')) {
  273. goog.asserts.assert(
  274. url instanceof goog.html.TrustedResourceUrl,
  275. 'URL must be TrustedResourceUrl because "rel" contains "stylesheet"');
  276. link.href = goog.html.TrustedResourceUrl.unwrap(url);
  277. } else if (url instanceof goog.html.TrustedResourceUrl) {
  278. link.href = goog.html.TrustedResourceUrl.unwrap(url);
  279. } else if (url instanceof goog.html.SafeUrl) {
  280. link.href = goog.html.SafeUrl.unwrap(url);
  281. } else { // string
  282. // SafeUrl.sanitize must return legitimate SafeUrl when passed a string.
  283. link.href = goog.html.SafeUrl.sanitize(url).getTypedStringValue();
  284. }
  285. };
  286. /**
  287. * Safely assigns a URL to an object element's data property.
  288. *
  289. * Example usage:
  290. * goog.dom.safe.setObjectData(objectEl, url);
  291. * which is a safe alternative to
  292. * objectEl.data = url;
  293. * The latter can result in loading untrusted code unless setit is ensured that
  294. * the URL refers to a trustworthy resource.
  295. *
  296. * @param {!HTMLObjectElement} object The object element whose data property
  297. * is to be assigned to.
  298. * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
  299. */
  300. goog.dom.safe.setObjectData = function(object, url) {
  301. goog.dom.asserts.assertIsHTMLObjectElement(object);
  302. object.data = goog.html.TrustedResourceUrl.unwrap(url);
  303. };
  304. /**
  305. * Safely assigns a URL to a script element's src property.
  306. *
  307. * Example usage:
  308. * goog.dom.safe.setScriptSrc(scriptEl, url);
  309. * which is a safe alternative to
  310. * scriptEl.src = url;
  311. * The latter can result in loading untrusted code unless it is ensured that
  312. * the URL refers to a trustworthy resource.
  313. *
  314. * @param {!HTMLScriptElement} script The script element whose src property
  315. * is to be assigned to.
  316. * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
  317. */
  318. goog.dom.safe.setScriptSrc = function(script, url) {
  319. goog.dom.asserts.assertIsHTMLScriptElement(script);
  320. script.src = goog.html.TrustedResourceUrl.unwrap(url);
  321. };
  322. /**
  323. * Safely assigns a value to a script element's content.
  324. *
  325. * Example usage:
  326. * goog.dom.safe.setScriptContent(scriptEl, content);
  327. * which is a safe alternative to
  328. * scriptEl.text = content;
  329. * The latter can result in executing untrusted code unless it is ensured that
  330. * the code is loaded from a trustworthy resource.
  331. *
  332. * @param {!HTMLScriptElement} script The script element whose content is being
  333. * set.
  334. * @param {!goog.html.SafeScript} content The content to assign.
  335. */
  336. goog.dom.safe.setScriptContent = function(script, content) {
  337. goog.dom.asserts.assertIsHTMLScriptElement(script);
  338. script.text = goog.html.SafeScript.unwrap(content);
  339. };
  340. /**
  341. * Safely assigns a URL to a Location object's href property.
  342. *
  343. * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
  344. * loc's href property. If url is of type string however, it is first sanitized
  345. * using goog.html.SafeUrl.sanitize.
  346. *
  347. * Example usage:
  348. * goog.dom.safe.setLocationHref(document.location, redirectUrl);
  349. * which is a safe alternative to
  350. * document.location.href = redirectUrl;
  351. * The latter can result in XSS vulnerabilities if redirectUrl is a
  352. * user-/attacker-controlled value.
  353. *
  354. * @param {!Location} loc The Location object whose href property is to be
  355. * assigned to.
  356. * @param {string|!goog.html.SafeUrl} url The URL to assign.
  357. * @see goog.html.SafeUrl#sanitize
  358. */
  359. goog.dom.safe.setLocationHref = function(loc, url) {
  360. goog.dom.asserts.assertIsLocation(loc);
  361. /** @type {!goog.html.SafeUrl} */
  362. var safeUrl;
  363. if (url instanceof goog.html.SafeUrl) {
  364. safeUrl = url;
  365. } else {
  366. safeUrl = goog.html.SafeUrl.sanitize(url);
  367. }
  368. loc.href = goog.html.SafeUrl.unwrap(safeUrl);
  369. };
  370. /**
  371. * Safely opens a URL in a new window (via window.open).
  372. *
  373. * If url is of type goog.html.SafeUrl, its value is unwrapped and passed in to
  374. * window.open. If url is of type string however, it is first sanitized
  375. * using goog.html.SafeUrl.sanitize.
  376. *
  377. * Note that this function does not prevent leakages via the referer that is
  378. * sent by window.open. It is advised to only use this to open 1st party URLs.
  379. *
  380. * Example usage:
  381. * goog.dom.safe.openInWindow(url);
  382. * which is a safe alternative to
  383. * window.open(url);
  384. * The latter can result in XSS vulnerabilities if redirectUrl is a
  385. * user-/attacker-controlled value.
  386. *
  387. * @param {string|!goog.html.SafeUrl} url The URL to open.
  388. * @param {Window=} opt_openerWin Window of which to call the .open() method.
  389. * Defaults to the global window.
  390. * @param {!goog.string.Const=} opt_name Name of the window to open in. Can be
  391. * _top, etc as allowed by window.open().
  392. * @param {string=} opt_specs Comma-separated list of specifications, same as
  393. * in window.open().
  394. * @param {boolean=} opt_replace Whether to replace the current entry in browser
  395. * history, same as in window.open().
  396. * @return {Window} Window the url was opened in.
  397. */
  398. goog.dom.safe.openInWindow = function(
  399. url, opt_openerWin, opt_name, opt_specs, opt_replace) {
  400. /** @type {!goog.html.SafeUrl} */
  401. var safeUrl;
  402. if (url instanceof goog.html.SafeUrl) {
  403. safeUrl = url;
  404. } else {
  405. safeUrl = goog.html.SafeUrl.sanitize(url);
  406. }
  407. var win = opt_openerWin || window;
  408. return win.open(
  409. goog.html.SafeUrl.unwrap(safeUrl),
  410. // If opt_name is undefined, simply passing that in to open() causes IE to
  411. // reuse the current window instead of opening a new one. Thus we pass ''
  412. // in instead, which according to spec opens a new window. See
  413. // https://html.spec.whatwg.org/multipage/browsers.html#dom-open .
  414. opt_name ? goog.string.Const.unwrap(opt_name) : '', opt_specs,
  415. opt_replace);
  416. };