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
40goog.provide('goog.dom.safe');
41goog.provide('goog.dom.safe.InsertAdjacentHtmlPosition');
42
43goog.require('goog.asserts');
44goog.require('goog.html.SafeHtml');
45goog.require('goog.html.SafeUrl');
46goog.require('goog.html.TrustedResourceUrl');
47goog.require('goog.string');
48goog.require('goog.string.Const');
49
50
51/** @enum {string} */
52goog.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 */
67goog.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 */
77goog.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 */
87goog.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 */
97goog.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 */
121goog.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 */
147goog.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 */
166goog.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 */
185goog.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 */
215goog.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 */
247goog.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 */
266goog.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 */
290goog.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 */
330goog.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};