Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/html/safeurl.js
2868 views
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 The SafeUrl type and its builders.
17
*
18
* TODO(xtof): Link to document stating type contract.
19
*/
20
21
goog.provide('goog.html.SafeUrl');
22
23
goog.require('goog.asserts');
24
goog.require('goog.fs.url');
25
goog.require('goog.html.TrustedResourceUrl');
26
goog.require('goog.i18n.bidi.Dir');
27
goog.require('goog.i18n.bidi.DirectionalString');
28
goog.require('goog.string');
29
goog.require('goog.string.Const');
30
goog.require('goog.string.TypedString');
31
32
33
34
/**
35
* A string that is safe to use in URL context in DOM APIs and HTML documents.
36
*
37
* A SafeUrl is a string-like object that carries the security type contract
38
* that its value as a string will not cause untrusted script execution
39
* when evaluated as a hyperlink URL in a browser.
40
*
41
* Values of this type are guaranteed to be safe to use in URL/hyperlink
42
* contexts, such as assignment to URL-valued DOM properties, in the sense that
43
* the use will not result in a Cross-Site-Scripting vulnerability. Similarly,
44
* SafeUrls can be interpolated into the URL context of an HTML template (e.g.,
45
* inside a href attribute). However, appropriate HTML-escaping must still be
46
* applied.
47
*
48
* Note that, as documented in {@code goog.html.SafeUrl.unwrap}, this type's
49
* contract does not guarantee that instances are safe to interpolate into HTML
50
* without appropriate escaping.
51
*
52
* Note also that this type's contract does not imply any guarantees regarding
53
* the resource the URL refers to. In particular, SafeUrls are <b>not</b>
54
* safe to use in a context where the referred-to resource is interpreted as
55
* trusted code, e.g., as the src of a script tag.
56
*
57
* Instances of this type must be created via the factory methods
58
* ({@code goog.html.SafeUrl.fromConstant}, {@code goog.html.SafeUrl.sanitize}),
59
* etc and not by invoking its constructor. The constructor intentionally
60
* takes no parameters and the type is immutable; hence only a default instance
61
* corresponding to the empty string can be obtained via constructor invocation.
62
*
63
* @see goog.html.SafeUrl#fromConstant
64
* @see goog.html.SafeUrl#from
65
* @see goog.html.SafeUrl#sanitize
66
* @constructor
67
* @final
68
* @struct
69
* @implements {goog.i18n.bidi.DirectionalString}
70
* @implements {goog.string.TypedString}
71
*/
72
goog.html.SafeUrl = function() {
73
/**
74
* The contained value of this SafeUrl. The field has a purposely ugly
75
* name to make (non-compiled) code that attempts to directly access this
76
* field stand out.
77
* @private {string}
78
*/
79
this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = '';
80
81
/**
82
* A type marker used to implement additional run-time type checking.
83
* @see goog.html.SafeUrl#unwrap
84
* @const {!Object}
85
* @private
86
*/
87
this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
88
goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
89
};
90
91
92
/**
93
* The innocuous string generated by goog.html.SafeUrl.sanitize when passed
94
* an unsafe URL.
95
*
96
* about:invalid is registered in
97
* http://www.w3.org/TR/css3-values/#about-invalid.
98
* http://tools.ietf.org/html/rfc6694#section-2.2.1 permits about URLs to
99
* contain a fragment, which is not to be considered when determining if an
100
* about URL is well-known.
101
*
102
* Using about:invalid seems preferable to using a fixed data URL, since
103
* browsers might choose to not report CSP violations on it, as legitimate
104
* CSS function calls to attr() can result in this URL being produced. It is
105
* also a standard URL which matches exactly the semantics we need:
106
* "The about:invalid URI references a non-existent document with a generic
107
* error condition. It can be used when a URI is necessary, but the default
108
* value shouldn't be resolveable as any type of document".
109
*
110
* @const {string}
111
*/
112
goog.html.SafeUrl.INNOCUOUS_STRING = 'about:invalid#zClosurez';
113
114
115
/**
116
* @override
117
* @const
118
*/
119
goog.html.SafeUrl.prototype.implementsGoogStringTypedString = true;
120
121
122
/**
123
* Returns this SafeUrl's value a string.
124
*
125
* IMPORTANT: In code where it is security relevant that an object's type is
126
* indeed {@code SafeUrl}, use {@code goog.html.SafeUrl.unwrap} instead of this
127
* method. If in doubt, assume that it's security relevant. In particular, note
128
* that goog.html functions which return a goog.html type do not guarantee that
129
* the returned instance is of the right type. For example:
130
*
131
* <pre>
132
* var fakeSafeHtml = new String('fake');
133
* fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
134
* var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
135
* // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
136
* // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof
137
* // goog.html.SafeHtml.
138
* </pre>
139
*
140
* IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
141
* behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST
142
* be appropriately escaped before embedding in a HTML document. Note that the
143
* required escaping is context-sensitive (e.g. a different escaping is
144
* required for embedding a URL in a style property within a style
145
* attribute, as opposed to embedding in a href attribute).
146
*
147
* @see goog.html.SafeUrl#unwrap
148
* @override
149
*/
150
goog.html.SafeUrl.prototype.getTypedStringValue = function() {
151
return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
152
};
153
154
155
/**
156
* @override
157
* @const
158
*/
159
goog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString = true;
160
161
162
/**
163
* Returns this URLs directionality, which is always {@code LTR}.
164
* @override
165
*/
166
goog.html.SafeUrl.prototype.getDirection = function() {
167
return goog.i18n.bidi.Dir.LTR;
168
};
169
170
171
if (goog.DEBUG) {
172
/**
173
* Returns a debug string-representation of this value.
174
*
175
* To obtain the actual string value wrapped in a SafeUrl, use
176
* {@code goog.html.SafeUrl.unwrap}.
177
*
178
* @see goog.html.SafeUrl#unwrap
179
* @override
180
*/
181
goog.html.SafeUrl.prototype.toString = function() {
182
return 'SafeUrl{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +
183
'}';
184
};
185
}
186
187
188
/**
189
* Performs a runtime check that the provided object is indeed a SafeUrl
190
* object, and returns its value.
191
*
192
* IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
193
* behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST
194
* be appropriately escaped before embedding in a HTML document. Note that the
195
* required escaping is context-sensitive (e.g. a different escaping is
196
* required for embedding a URL in a style property within a style
197
* attribute, as opposed to embedding in a href attribute).
198
*
199
* @param {!goog.html.SafeUrl} safeUrl The object to extract from.
200
* @return {string} The SafeUrl object's contained string, unless the run-time
201
* type check fails. In that case, {@code unwrap} returns an innocuous
202
* string, or, if assertions are enabled, throws
203
* {@code goog.asserts.AssertionError}.
204
*/
205
goog.html.SafeUrl.unwrap = function(safeUrl) {
206
// Perform additional Run-time type-checking to ensure that safeUrl is indeed
207
// an instance of the expected type. This provides some additional protection
208
// against security bugs due to application code that disables type checks.
209
// Specifically, the following checks are performed:
210
// 1. The object is an instance of the expected type.
211
// 2. The object is not an instance of a subclass.
212
// 3. The object carries a type marker for the expected type. "Faking" an
213
// object requires a reference to the type marker, which has names intended
214
// to stand out in code reviews.
215
if (safeUrl instanceof goog.html.SafeUrl &&
216
safeUrl.constructor === goog.html.SafeUrl &&
217
safeUrl.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
218
goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
219
return safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
220
} else {
221
goog.asserts.fail('expected object of type SafeUrl, got \'' +
222
safeUrl + '\' of type ' + goog.typeOf(safeUrl));
223
return 'type_error:SafeUrl';
224
}
225
};
226
227
228
/**
229
* Creates a SafeUrl object from a compile-time constant string.
230
*
231
* Compile-time constant strings are inherently program-controlled and hence
232
* trusted.
233
*
234
* @param {!goog.string.Const} url A compile-time-constant string from which to
235
* create a SafeUrl.
236
* @return {!goog.html.SafeUrl} A SafeUrl object initialized to {@code url}.
237
*/
238
goog.html.SafeUrl.fromConstant = function(url) {
239
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
240
goog.string.Const.unwrap(url));
241
};
242
243
244
/**
245
* A pattern that matches Blob or data types that can have SafeUrls created
246
* from URL.createObjectURL(blob) or via a data: URI. Only matches image and
247
* video types, currently.
248
* @const
249
* @private
250
*/
251
goog.html.SAFE_MIME_TYPE_PATTERN_ =
252
/^(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm))$/i;
253
254
255
/**
256
* Creates a SafeUrl wrapping a blob URL for the given {@code blob}.
257
*
258
* The blob URL is created with {@code URL.createObjectURL}. If the MIME type
259
* for {@code blob} is not of a known safe image or video MIME type, then the
260
* SafeUrl will wrap {@link #INNOCUOUS_STRING}.
261
*
262
* @see http://www.w3.org/TR/FileAPI/#url
263
* @param {!Blob} blob
264
* @return {!goog.html.SafeUrl} The blob URL, or an innocuous string wrapped
265
* as a SafeUrl.
266
*/
267
goog.html.SafeUrl.fromBlob = function(blob) {
268
var url = goog.html.SAFE_MIME_TYPE_PATTERN_.test(blob.type) ?
269
goog.fs.url.createObjectUrl(blob) :
270
goog.html.SafeUrl.INNOCUOUS_STRING;
271
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
272
};
273
274
275
/**
276
* Matches a base-64 data URL, with the first match group being the MIME type.
277
* @const
278
* @private
279
*/
280
goog.html.DATA_URL_PATTERN_ = /^data:([^;,]*);base64,[a-z0-9+\/]+=*$/i;
281
282
283
/**
284
* Creates a SafeUrl wrapping a data: URL, after validating it matches a
285
* known-safe image or video MIME type.
286
*
287
* @param {string} dataUrl A valid base64 data URL with one of the whitelisted
288
* image or video MIME types.
289
* @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
290
* wrapped as a SafeUrl if it does not pass.
291
*/
292
goog.html.SafeUrl.fromDataUrl = function(dataUrl) {
293
// There's a slight risk here that a browser sniffs the content type if it
294
// doesn't know the MIME type and executes HTML within the data: URL. For this
295
// to cause XSS it would also have to execute the HTML in the same origin
296
// of the page with the link. It seems unlikely that both of these will
297
// happen, particularly in not really old IEs.
298
var match = dataUrl.match(goog.html.DATA_URL_PATTERN_);
299
var valid = match && goog.html.SAFE_MIME_TYPE_PATTERN_.test(match[1]);
300
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
301
valid ? dataUrl : goog.html.SafeUrl.INNOCUOUS_STRING);
302
};
303
304
305
/**
306
* Creates a SafeUrl wrapping a tel: URL.
307
*
308
* @param {string} telUrl A tel URL.
309
* @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
310
* wrapped as a SafeUrl if it does not pass.
311
*/
312
goog.html.SafeUrl.fromTelUrl = function(telUrl) {
313
// There's a risk that a tel: URL could immediately place a call once
314
// clicked, without requiring user confirmation. For that reason it is
315
// handled in this separate function.
316
if (!goog.string.caseInsensitiveStartsWith(telUrl, 'tel:')) {
317
telUrl = goog.html.SafeUrl.INNOCUOUS_STRING;
318
}
319
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
320
telUrl);
321
};
322
323
324
/**
325
* Creates a SafeUrl from TrustedResourceUrl. This is safe because
326
* TrustedResourceUrl is more tightly restricted than SafeUrl.
327
*
328
* @param {!goog.html.TrustedResourceUrl} trustedResourceUrl
329
* @return {!goog.html.SafeUrl}
330
*/
331
goog.html.SafeUrl.fromTrustedResourceUrl = function(trustedResourceUrl) {
332
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
333
goog.html.TrustedResourceUrl.unwrap(trustedResourceUrl));
334
};
335
336
337
/**
338
* A pattern that recognizes a commonly useful subset of URLs that satisfy
339
* the SafeUrl contract.
340
*
341
* This regular expression matches a subset of URLs that will not cause script
342
* execution if used in URL context within a HTML document. Specifically, this
343
* regular expression matches if (comment from here on and regex copied from
344
* Soy's EscapingConventions):
345
* (1) Either a protocol in a whitelist (http, https, mailto or ftp).
346
* (2) or no protocol. A protocol must be followed by a colon. The below
347
* allows that by allowing colons only after one of the characters [/?#].
348
* A colon after a hash (#) must be in the fragment.
349
* Otherwise, a colon after a (?) must be in a query.
350
* Otherwise, a colon after a single solidus (/) must be in a path.
351
* Otherwise, a colon after a double solidus (//) must be in the authority
352
* (before port).
353
*
354
* @private
355
* @const {!RegExp}
356
*/
357
goog.html.SAFE_URL_PATTERN_ =
358
/^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;
359
360
361
/**
362
* Creates a SafeUrl object from {@code url}. If {@code url} is a
363
* goog.html.SafeUrl then it is simply returned. Otherwise the input string is
364
* validated to match a pattern of commonly used safe URLs.
365
*
366
* {@code url} may be a URL with the http, https, mailto or ftp scheme,
367
* or a relative URL (i.e., a URL without a scheme; specifically, a
368
* scheme-relative, absolute-path-relative, or path-relative URL).
369
*
370
* @see http://url.spec.whatwg.org/#concept-relative-url
371
* @param {string|!goog.string.TypedString} url The URL to validate.
372
* @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.
373
*/
374
goog.html.SafeUrl.sanitize = function(url) {
375
if (url instanceof goog.html.SafeUrl) {
376
return url;
377
} else if (url.implementsGoogStringTypedString) {
378
url = url.getTypedStringValue();
379
} else {
380
url = String(url);
381
}
382
if (!goog.html.SAFE_URL_PATTERN_.test(url)) {
383
url = goog.html.SafeUrl.INNOCUOUS_STRING;
384
}
385
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
386
};
387
388
389
/**
390
* Type marker for the SafeUrl type, used to implement additional run-time
391
* type checking.
392
* @const {!Object}
393
* @private
394
*/
395
goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
396
397
398
/**
399
* Package-internal utility method to create SafeUrl instances.
400
*
401
* @param {string} url The string to initialize the SafeUrl object with.
402
* @return {!goog.html.SafeUrl} The initialized SafeUrl object.
403
* @package
404
*/
405
goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse = function(
406
url) {
407
var safeUrl = new goog.html.SafeUrl();
408
safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = url;
409
return safeUrl;
410
};
411
412
413
/**
414
* A SafeUrl corresponding to the special about:blank url.
415
* @const {!goog.html.SafeUrl}
416
*/
417
goog.html.SafeUrl.ABOUT_BLANK =
418
goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
419
'about:blank');
420
421