# lib/goog/dom/safe.js

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