Path: blob/trunk/third_party/closure/goog/html/safeurl.js
4069 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview The SafeUrl type and its builders.8*9* TODO(xtof): Link to document stating type contract.10*/1112goog.provide('goog.html.SafeUrl');1314goog.require('goog.asserts');15goog.require('goog.fs.url');16goog.require('goog.html.TrustedResourceUrl');17goog.require('goog.string.Const');18goog.require('goog.string.TypedString');19goog.require('goog.string.internal');20goog.require('goog.utils');21222324/**25* A string that is safe to use in URL context in DOM APIs and HTML documents.26*27* A SafeUrl is a string-like object that carries the security type contract28* that its value as a string will not cause untrusted script execution29* when evaluated as a hyperlink URL in a browser.30*31* Values of this type are guaranteed to be safe to use in URL/hyperlink32* contexts, such as assignment to URL-valued DOM properties, in the sense that33* the use will not result in a Cross-Site-Scripting vulnerability. Similarly,34* SafeUrls can be interpolated into the URL context of an HTML template (e.g.,35* inside a href attribute). However, appropriate HTML-escaping must still be36* applied.37*38* Note that, as documented in `goog.html.SafeUrl.unwrap`, this type's39* contract does not guarantee that instances are safe to interpolate into HTML40* without appropriate escaping.41*42* Note also that this type's contract does not imply any guarantees regarding43* the resource the URL refers to. In particular, SafeUrls are <b>not</b>44* safe to use in a context where the referred-to resource is interpreted as45* trusted code, e.g., as the src of a script tag.46*47* Instances of this type must be created via the factory methods48* (`goog.html.SafeUrl.fromConstant`, `goog.html.SafeUrl.sanitize`),49* etc and not by invoking its constructor. The constructor intentionally takes50* an extra parameter that cannot be constructed outside of this file and the51* type is immutable; hence only a default instance corresponding to the empty52* string can be obtained via constructor invocation.53*54* @see goog.html.SafeUrl#fromConstant55* @see goog.html.SafeUrl#from56* @see goog.html.SafeUrl#sanitize57* @final58* @struct59* @implements {goog.string.TypedString}60*/61goog.html.SafeUrl = class {62/**63* @param {string} value64* @param {!Object} token package-internal implementation detail.65*/66constructor(value, token) {67if (goog.DEBUG && token !== goog.html.SafeUrl.CONSTRUCTOR_TOKEN_PRIVATE_) {68throw Error('SafeUrl is not meant to be built directly');69}7071/**72* The contained value of this SafeUrl. The field has a purposely ugly73* name to make (non-compiled) code that attempts to directly access this74* field stand out.75* @const76* @private {string}77*/78this.privateDoNotAccessOrElseSafeUrlWrappedValue_ = value;79}8081/**82* Returns a string-representation of this value.83*84* To obtain the actual string value wrapped in a SafeUrl, use85* `goog.html.SafeUrl.unwrap`.86*87* @return {string}88* @see goog.html.SafeUrl#unwrap89* @override90*/91toString() {92return this.privateDoNotAccessOrElseSafeUrlWrappedValue_.toString();93}94};959697/**98* The innocuous string generated by goog.html.SafeUrl.sanitize when passed99* an unsafe URL.100*101* about:invalid is registered in102* http://www.w3.org/TR/css3-values/#about-invalid.103* http://tools.ietf.org/html/rfc6694#section-2.2.1 permits about URLs to104* contain a fragment, which is not to be considered when determining if an105* about URL is well-known.106*107* Using about:invalid seems preferable to using a fixed data URL, since108* browsers might choose to not report CSP violations on it, as legitimate109* CSS function calls to attr() can result in this URL being produced. It is110* also a standard URL which matches exactly the semantics we need:111* "The about:invalid URI references a non-existent document with a generic112* error condition. It can be used when a URI is necessary, but the default113* value shouldn't be resolveable as any type of document".114*115* @const {string}116*/117goog.html.SafeUrl.INNOCUOUS_STRING = 'about:invalid#zClosurez';118119120/**121* @override122* @const123*/124goog.html.SafeUrl.prototype.implementsGoogStringTypedString = true;125126127/**128* Returns this SafeUrl's value as a string.129*130* IMPORTANT: In code where it is security relevant that an object's type is131* indeed `SafeUrl`, use `goog.html.SafeUrl.unwrap` instead of this132* method. If in doubt, assume that it's security relevant. In particular, note133* that goog.html functions which return a goog.html type do not guarantee that134* the returned instance is of the right type.135*136* IMPORTANT: The guarantees of the SafeUrl type contract only extend to the137* behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST138* be appropriately escaped before embedding in a HTML document. Note that the139* required escaping is context-sensitive (e.g. a different escaping is140* required for embedding a URL in a style property within a style141* attribute, as opposed to embedding in a href attribute).142*143* @see goog.html.SafeUrl#unwrap144* @override145*/146goog.html.SafeUrl.prototype.getTypedStringValue = function() {147'use strict';148return this.privateDoNotAccessOrElseSafeUrlWrappedValue_.toString();149};150151/**152* Performs a runtime check that the provided object is indeed a SafeUrl153* object, and returns its value.154*155* IMPORTANT: The guarantees of the SafeUrl type contract only extend to the156* behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST157* be appropriately escaped before embedding in a HTML document. Note that the158* required escaping is context-sensitive (e.g. a different escaping is159* required for embedding a URL in a style property within a style160* attribute, as opposed to embedding in a href attribute).161*162* @param {!goog.html.SafeUrl} safeUrl The object to extract from.163* @return {string} The SafeUrl object's contained string, unless the run-time164* type check fails. In that case, `unwrap` returns an innocuous165* string, or, if assertions are enabled, throws166* `goog.asserts.AssertionError`.167*/168goog.html.SafeUrl.unwrap = function(safeUrl) {169'use strict';170// Perform additional Run-time type-checking to ensure that safeUrl is indeed171// an instance of the expected type. This provides some additional protection172// against security bugs due to application code that disables type checks.173// Specifically, the following checks are performed:174// 1. The object is an instance of the expected type.175// 2. The object is not an instance of a subclass.176if (safeUrl instanceof goog.html.SafeUrl &&177safeUrl.constructor === goog.html.SafeUrl) {178return safeUrl.privateDoNotAccessOrElseSafeUrlWrappedValue_;179} else {180goog.asserts.fail(181'expected object of type SafeUrl, got \'' + safeUrl + '\' of type ' +182goog.utils.typeOf(safeUrl));183return 'type_error:SafeUrl';184}185};186187188/**189* Creates a SafeUrl object from a compile-time constant string.190*191* Compile-time constant strings are inherently program-controlled and hence192* trusted.193*194* @param {!goog.string.Const} url A compile-time-constant string from which to195* create a SafeUrl.196* @return {!goog.html.SafeUrl} A SafeUrl object initialized to `url`.197*/198goog.html.SafeUrl.fromConstant = function(url) {199'use strict';200return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(201goog.string.Const.unwrap(url));202};203204205/**206* A pattern that matches Blob or data types that can have SafeUrls created207* from URL.createObjectURL(blob) or via a data: URI.208*209* This has some parameter support (most notably, we haven't implemented the210* more complex parts like %-encoded characters or non-alphanumerical ones for211* simplicity's sake). The specs are fairly complex, and they don't212* always match Chrome's behavior: we settled on a subset where we're confident213* all parties involved agree.214*215* The spec is available at https://mimesniff.spec.whatwg.org/ (and see216* https://tools.ietf.org/html/rfc2397 for data: urls, which override some of217* it).218* @const219* @private220*/221goog.html.SAFE_MIME_TYPE_PATTERN_ = new RegExp(222// Note: Due to content-sniffing concerns, only add MIME types for223// media formats.224'^(?:audio/(?:3gpp2|3gpp|aac|L16|midi|mp3|mp4|mpeg|oga|ogg|opus|x-m4a|x-matroska|x-wav|wav|webm)|' +225'font/\\w+|' +226'image/(?:bmp|gif|jpeg|jpg|png|tiff|webp|x-icon|heic|heif)|' +227'video/(?:mpeg|mp4|ogg|webm|quicktime|x-matroska))' +228'(?:;\\w+=(?:\\w+|"[\\w;,= ]+"))*$', // MIME type parameters229'i');230231232/**233* @param {string} mimeType The MIME type to check if safe.234* @return {boolean} True if the MIME type is safe and creating a Blob via235* `SafeUrl.fromBlob()` with that type will not fail due to the type. False236* otherwise.237*/238goog.html.SafeUrl.isSafeMimeType = function(mimeType) {239'use strict';240return goog.html.SAFE_MIME_TYPE_PATTERN_.test(mimeType);241};242243244/**245* Creates a SafeUrl wrapping a blob URL for the given `blob`.246*247* The blob URL is created with `URL.createObjectURL`. If the MIME type248* for `blob` is not of a known safe audio, image or video MIME type,249* then the SafeUrl will wrap {@link #INNOCUOUS_STRING}.250*251* Note: Call {@link revokeObjectUrl} on the URL after it's used252* to prevent memory leaks.253*254* @see http://www.w3.org/TR/FileAPI/#url255* @param {!Blob} blob256* @return {!goog.html.SafeUrl} The blob URL, or an innocuous string wrapped257* as a SafeUrl.258*/259goog.html.SafeUrl.fromBlob = function(blob) {260'use strict';261var url = goog.html.SafeUrl.isSafeMimeType(blob.type) ?262goog.fs.url.createObjectUrl(blob) :263goog.html.SafeUrl.INNOCUOUS_STRING;264return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);265};266267268/**269* Revokes an object URL created for a safe URL created {@link fromBlob()}.270* @param {!goog.html.SafeUrl} safeUrl SafeUrl wrapping a blob object.271* @return {void}272*/273goog.html.SafeUrl.revokeObjectUrl = function(safeUrl) {274'use strict';275var url = safeUrl.getTypedStringValue();276if (url !== goog.html.SafeUrl.INNOCUOUS_STRING) {277goog.fs.url.revokeObjectUrl(url);278}279};280281282/**283* Creates a SafeUrl wrapping a blob URL created for a MediaSource.284* @param {!MediaSource} mediaSource285* @return {!goog.html.SafeUrl} The blob URL.286*/287goog.html.SafeUrl.fromMediaSource = function(mediaSource) {288'use strict';289goog.asserts.assert(290'MediaSource' in goog.global, 'No support for MediaSource');291const url = mediaSource instanceof MediaSource ?292goog.fs.url.createObjectUrl(mediaSource) :293goog.html.SafeUrl.INNOCUOUS_STRING;294return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);295};296297298/**299* Matches a base-64 data URL, with the first match group being the MIME type.300* @const301* @private302*/303goog.html.DATA_URL_PATTERN_ = /^data:(.*);base64,[a-z0-9+\/]+=*$/i;304305306/**307* Attempts to create a SafeUrl wrapping a `data:` URL, after validating it308* matches a known-safe media MIME type. If it doesn't match, return `null`.309*310* @param {string} dataUrl A valid base64 data URL with one of the whitelisted311* media MIME types.312* @return {?goog.html.SafeUrl} A matching safe URL, or `null` if it does not313* pass.314*/315goog.html.SafeUrl.tryFromDataUrl = function(dataUrl) {316'use strict';317// For defensive purposes, in case users cast around the parameter type.318dataUrl = String(dataUrl);319// RFC4648 suggest to ignore CRLF in base64 encoding.320// See https://tools.ietf.org/html/rfc4648.321// Remove the CR (%0D) and LF (%0A) from the dataUrl.322var filteredDataUrl = dataUrl.replace(/(%0A|%0D)/g, '');323var match = filteredDataUrl.match(goog.html.DATA_URL_PATTERN_);324// Note: The only risk of XSS here is if the `data:` URL results in a325// same-origin document. In which case content-sniffing might cause the326// browser to interpret the contents as html.327// All modern browsers consider `data:` URL documents to have unique empty328// origins. Only Firefox for versions prior to v57 behaves differently:329// https://blog.mozilla.org/security/2017/10/04/treating-data-urls-unique-origins-firefox-57/330// Older versions of IE don't understand `data:` urls, so it is not an issue.331if (match) {332return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(333filteredDataUrl);334}335return null;336};337338339/**340* Creates a SafeUrl wrapping a `data:` URL, after validating it matches a341* known-safe media MIME type. If it doesn't match, return342* `goog.html.SafeUrl.INNOCUOUS_URL`.343*344* @param {string} dataUrl A valid base64 data URL with one of the whitelisted345* media MIME types.346* @return {!goog.html.SafeUrl} A matching safe URL, or347* `goog.html.SafeUrl.INNOCUOUS_URL` if it does not pass.348*/349goog.html.SafeUrl.fromDataUrl = function(dataUrl) {350'use strict';351return goog.html.SafeUrl.tryFromDataUrl(dataUrl) ||352goog.html.SafeUrl.INNOCUOUS_URL;353};354355356/**357* Creates a SafeUrl wrapping a tel: URL.358*359* @param {string} telUrl A tel URL.360* @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}361* wrapped as a SafeUrl if it does not pass.362*/363goog.html.SafeUrl.fromTelUrl = function(telUrl) {364'use strict';365// There's a risk that a tel: URL could immediately place a call once366// clicked, without requiring user confirmation. For that reason it is367// handled in this separate function.368if (!goog.string.internal.caseInsensitiveStartsWith(telUrl, 'tel:')) {369telUrl = goog.html.SafeUrl.INNOCUOUS_STRING;370}371return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(372telUrl);373};374375376/**377* Matches a sip/sips URL. We only allow urls that consist of an email address.378* The characters '?' and '#' are not allowed in the local part of the email379* address.380* @const381* @private382*/383goog.html.SIP_URL_PATTERN_ = new RegExp(384'^sip[s]?:[+a-z0-9_.!$%&\'*\\/=^`{|}~-]+@([a-z0-9-]+\\.)+[a-z0-9]{2,63}$',385'i');386387388/**389* Creates a SafeUrl wrapping a sip: URL. We only allow urls that consist of an390* email address. The characters '?' and '#' are not allowed in the local part391* of the email address.392*393* @param {string} sipUrl A sip URL.394* @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}395* wrapped as a SafeUrl if it does not pass.396*/397goog.html.SafeUrl.fromSipUrl = function(sipUrl) {398'use strict';399if (!goog.html.SIP_URL_PATTERN_.test(decodeURIComponent(sipUrl))) {400sipUrl = goog.html.SafeUrl.INNOCUOUS_STRING;401}402return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(403sipUrl);404};405406407/**408* Creates a SafeUrl wrapping a fb-messenger://share URL.409*410* @param {string} facebookMessengerUrl A facebook messenger URL.411* @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}412* wrapped as a SafeUrl if it does not pass.413*/414goog.html.SafeUrl.fromFacebookMessengerUrl = function(facebookMessengerUrl) {415'use strict';416if (!goog.string.internal.caseInsensitiveStartsWith(417facebookMessengerUrl, 'fb-messenger://share')) {418facebookMessengerUrl = goog.html.SafeUrl.INNOCUOUS_STRING;419}420return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(421facebookMessengerUrl);422};423424/**425* Creates a SafeUrl wrapping a whatsapp://send URL.426*427* @param {string} whatsAppUrl A WhatsApp URL.428* @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}429* wrapped as a SafeUrl if it does not pass.430*/431goog.html.SafeUrl.fromWhatsAppUrl = function(whatsAppUrl) {432'use strict';433if (!goog.string.internal.caseInsensitiveStartsWith(434whatsAppUrl, 'whatsapp://send')) {435whatsAppUrl = goog.html.SafeUrl.INNOCUOUS_STRING;436}437return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(438whatsAppUrl);439};440441/**442* Creates a SafeUrl wrapping a sms: URL.443*444* @param {string} smsUrl A sms URL.445* @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}446* wrapped as a SafeUrl if it does not pass.447*/448goog.html.SafeUrl.fromSmsUrl = function(smsUrl) {449'use strict';450if (!goog.string.internal.caseInsensitiveStartsWith(smsUrl, 'sms:') ||451!goog.html.SafeUrl.isSmsUrlBodyValid_(smsUrl)) {452smsUrl = goog.html.SafeUrl.INNOCUOUS_STRING;453}454return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(455smsUrl);456};457458459/**460* Validates SMS URL `body` parameter, which is optional and should appear at461* most once and should be percent-encoded if present. Rejects many malformed462* bodies, but may spuriously reject some URLs and does not reject all malformed463* sms: URLs.464*465* @param {string} smsUrl A sms URL.466* @return {boolean} Whether SMS URL has a valid `body` parameter if it exists.467* @private468*/469goog.html.SafeUrl.isSmsUrlBodyValid_ = function(smsUrl) {470'use strict';471var hash = smsUrl.indexOf('#');472if (hash > 0) {473smsUrl = smsUrl.substring(0, hash);474}475var bodyParams = smsUrl.match(/[?&]body=/gi);476// "body" param is optional477if (!bodyParams) {478return true;479}480// "body" MUST only appear once481if (bodyParams.length > 1) {482return false;483}484// Get the encoded `body` parameter value.485var bodyValue = smsUrl.match(/[?&]body=([^&]*)/)[1];486if (!bodyValue) {487return true;488}489try {490decodeURIComponent(bodyValue);491} catch (error) {492return false;493}494return /^(?:[a-z0-9\-_.~]|%[0-9a-f]{2})+$/i.test(bodyValue);495};496497498/**499* Creates a SafeUrl wrapping a ssh: URL.500*501* @param {string} sshUrl A ssh URL.502* @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}503* wrapped as a SafeUrl if it does not pass.504*/505goog.html.SafeUrl.fromSshUrl = function(sshUrl) {506'use strict';507if (!goog.string.internal.caseInsensitiveStartsWith(sshUrl, 'ssh://')) {508sshUrl = goog.html.SafeUrl.INNOCUOUS_STRING;509}510return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(511sshUrl);512};513514/**515* Sanitizes a Chrome extension URL to SafeUrl, given a compile-time-constant516* extension identifier. Can also be restricted to chrome extensions.517*518* @param {string} url The url to sanitize. Should start with the extension519* scheme and the extension identifier.520* @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The521* extension id to accept, as a compile-time constant or an array of those.522*523* @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or524* `INNOCUOUS_STRING` if it's not.525*/526goog.html.SafeUrl.sanitizeChromeExtensionUrl = function(url, extensionId) {527'use strict';528return goog.html.SafeUrl.sanitizeExtensionUrl_(529/^chrome-extension:\/\/([^\/]+)\//, url, extensionId);530};531532/**533* Sanitizes a Firefox extension URL to SafeUrl, given a compile-time-constant534* extension identifier. Can also be restricted to chrome extensions.535*536* @param {string} url The url to sanitize. Should start with the extension537* scheme and the extension identifier.538* @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The539* extension id to accept, as a compile-time constant or an array of those.540*541* @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or542* `INNOCUOUS_STRING` if it's not.543*/544goog.html.SafeUrl.sanitizeFirefoxExtensionUrl = function(url, extensionId) {545'use strict';546return goog.html.SafeUrl.sanitizeExtensionUrl_(547/^moz-extension:\/\/([^\/]+)\//, url, extensionId);548};549550/**551* Sanitizes a Edge extension URL to SafeUrl, given a compile-time-constant552* extension identifier. Can also be restricted to chrome extensions.553*554* @param {string} url The url to sanitize. Should start with the extension555* scheme and the extension identifier.556* @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The557* extension id to accept, as a compile-time constant or an array of those.558*559* @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or560* `INNOCUOUS_STRING` if it's not.561*/562goog.html.SafeUrl.sanitizeEdgeExtensionUrl = function(url, extensionId) {563'use strict';564return goog.html.SafeUrl.sanitizeExtensionUrl_(565/^ms-browser-extension:\/\/([^\/]+)\//, url, extensionId);566};567568/**569* Private helper for converting extension URLs to SafeUrl, given the scheme for570* that particular extension type. Use the sanitizeFirefoxExtensionUrl,571* sanitizeChromeExtensionUrl or sanitizeEdgeExtensionUrl unless you're building572* new helpers.573*574* @private575* @param {!RegExp} scheme The scheme to accept as a RegExp extracting the576* extension identifier.577* @param {string} url The url to sanitize. Should start with the extension578* scheme and the extension identifier.579* @param {!goog.string.Const|!Array<!goog.string.Const>} extensionId The580* extension id to accept, as a compile-time constant or an array of those.581*582* @return {!goog.html.SafeUrl} Either `url` if it's deemed safe, or583* `INNOCUOUS_STRING` if it's not.584*/585goog.html.SafeUrl.sanitizeExtensionUrl_ = function(scheme, url, extensionId) {586'use strict';587var matches = scheme.exec(url);588if (!matches) {589url = goog.html.SafeUrl.INNOCUOUS_STRING;590} else {591var extractedExtensionId = matches[1];592var acceptedExtensionIds;593if (extensionId instanceof goog.string.Const) {594acceptedExtensionIds = [goog.string.Const.unwrap(extensionId)];595} else {596acceptedExtensionIds = extensionId.map(function unwrap(x) {597'use strict';598return goog.string.Const.unwrap(x);599});600}601if (acceptedExtensionIds.indexOf(extractedExtensionId) == -1) {602url = goog.html.SafeUrl.INNOCUOUS_STRING;603}604}605return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);606};607608609/**610* Creates a SafeUrl from TrustedResourceUrl. This is safe because611* TrustedResourceUrl is more tightly restricted than SafeUrl.612*613* @param {!goog.html.TrustedResourceUrl} trustedResourceUrl614* @return {!goog.html.SafeUrl}615*/616goog.html.SafeUrl.fromTrustedResourceUrl = function(trustedResourceUrl) {617'use strict';618return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(619goog.html.TrustedResourceUrl.unwrap(trustedResourceUrl));620};621622623/**624* A pattern that recognizes a commonly useful subset of URLs that satisfy625* the SafeUrl contract.626*627* This regular expression matches a subset of URLs that will not cause script628* execution if used in URL context within a HTML document. Specifically, this629* regular expression matches if (comment from here on and regex copied from630* Soy's EscapingConventions):631* (1) Either a protocol in a whitelist (http, https, mailto or ftp).632* (2) or no protocol. A protocol must be followed by a colon. The below633* allows that by allowing colons only after one of the characters [/?#].634* A colon after a hash (#) must be in the fragment.635* Otherwise, a colon after a (?) must be in a query.636* Otherwise, a colon after a single solidus (/) must be in a path.637* Otherwise, a colon after a double solidus (//) must be in the authority638* (before port).639*640* @private641* @const {!RegExp}642*/643goog.html.SAFE_URL_PATTERN_ =644/^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;645646/**647* Public version of goog.html.SAFE_URL_PATTERN_. Updating648* goog.html.SAFE_URL_PATTERN_ doesn't seem to be backward compatible.649* Namespace is also changed to goog.html.SafeUrl so it can be imported using650* goog.require('goog.dom.SafeUrl').651*652* TODO(bangert): Remove SAFE_URL_PATTERN_653* @const {!RegExp}654*/655goog.html.SafeUrl.SAFE_URL_PATTERN = goog.html.SAFE_URL_PATTERN_;656657/**658* Attempts to create a SafeUrl object from `url`. The input string is validated659* to match a pattern of commonly used safe URLs. If validation fails, `null` is660* returned.661*662* `url` may be a URL with the `http:`, `https:`, `mailto:`, `ftp:` or `data`663* scheme, or a relative URL (i.e., a URL without a scheme; specifically, a664* scheme-relative, absolute-path-relative, or path-relative URL).665*666* @see http://url.spec.whatwg.org/#concept-relative-url667* @param {string|!goog.string.TypedString} url The URL to validate.668* @return {?goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl, or null669* if validation fails.670*/671goog.html.SafeUrl.trySanitize = function(url) {672'use strict';673if (url instanceof goog.html.SafeUrl) {674return url;675}676if (typeof url == 'object' && url.implementsGoogStringTypedString) {677url = /** @type {!goog.string.TypedString} */ (url).getTypedStringValue();678} else {679// For defensive purposes, in case users cast around the parameter type.680url = String(url);681}682if (!goog.html.SAFE_URL_PATTERN_.test(url)) {683return goog.html.SafeUrl.tryFromDataUrl(url);684}685return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);686};687688/**689* Creates a SafeUrl object from `url`. If `url` is a690* `goog.html.SafeUrl` then it is simply returned. Otherwise the input string is691* validated to match a pattern of commonly used safe URLs. If validation fails,692* `goog.html.SafeUrl.INNOCUOUS_URL` is returned.693*694* `url` may be a URL with the `http:`, `https:`, `mailto:`, `ftp:` or `data`695* scheme, or a relative URL (i.e., a URL without a scheme; specifically, a696* scheme-relative, absolute-path-relative, or path-relative URL).697*698* @see http://url.spec.whatwg.org/#concept-relative-url699* @param {string|!goog.string.TypedString} url The URL to validate.700* @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.701*/702goog.html.SafeUrl.sanitize = function(url) {703'use strict';704return goog.html.SafeUrl.trySanitize(url) || goog.html.SafeUrl.INNOCUOUS_URL;705};706707/**708* Creates a SafeUrl object from `url`. If `url` is a709* `goog.html.SafeUrl` then it is simply returned. Otherwise the input string is710* validated to match a pattern of commonly used safe URLs.711*712* `url` may be a URL with the http, https, mailto or ftp scheme,713* or a relative URL (i.e., a URL without a scheme; specifically, a714* scheme-relative, absolute-path-relative, or path-relative URL).715*716* This function asserts (using goog.asserts) that the URL matches this pattern.717* If it does not, in addition to failing the assert, an innocuous URL will be718* returned.719*720* @see http://url.spec.whatwg.org/#concept-relative-url721* @param {string|!goog.string.TypedString} url The URL to validate.722* @param {boolean=} opt_allowDataUrl Whether to allow valid data: URLs.723* @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.724*/725goog.html.SafeUrl.sanitizeAssertUnchanged = function(url, opt_allowDataUrl) {726'use strict';727if (url instanceof goog.html.SafeUrl) {728return url;729} else if (typeof url == 'object' && url.implementsGoogStringTypedString) {730url = /** @type {!goog.string.TypedString} */ (url).getTypedStringValue();731} else {732url = String(url);733}734if (opt_allowDataUrl && /^data:/i.test(url)) {735var safeUrl = goog.html.SafeUrl.fromDataUrl(url);736if (safeUrl.getTypedStringValue() == url) {737return safeUrl;738}739}740if (!goog.asserts.assert(741goog.html.SAFE_URL_PATTERN_.test(url),742'%s does not match the safe URL pattern', url)) {743url = goog.html.SafeUrl.INNOCUOUS_STRING;744}745return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);746};747748/**749* Extracts the scheme from the given URL. If the URL is relative, https: is750* assumed.751* @param {string} url The URL to extract the scheme from.752* @return {string|undefined} the URL scheme.753*/754goog.html.SafeUrl.extractScheme = function(url) {755let parsedUrl;756try {757parsedUrl = new URL(url);758} catch (e) {759// According to https://url.spec.whatwg.org/#constructors, the URL760// constructor with one parameter throws if `url` is not absolute. In this761// case, we are sure that no explicit scheme (javascript: ) is set.762// This can also be a URL parsing error, but in this case the URL won't be763// run anyway.764return 'https:';765}766return parsedUrl.protocol;767};768769/**770* Creates a SafeUrl object from `url`. If `url` is a771* `goog.html.SafeUrl` then it is simply returned. Otherwise javascript: URLs772* are rejected.773*774* This function asserts (using goog.asserts) that the URL scheme is not775* javascript. If it is, in addition to failing the assert, an innocuous URL776* will be returned.777*778* @see http://url.spec.whatwg.org/#concept-relative-url779* @param {string|!goog.string.TypedString} url The URL to validate.780* @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.781*/782goog.html.SafeUrl.sanitizeJavascriptUrlAssertUnchanged = function(url) {783'use strict';784if (url instanceof goog.html.SafeUrl) {785return url;786} else if (typeof url == 'object' && url.implementsGoogStringTypedString) {787url = /** @type {!goog.string.TypedString} */ (url).getTypedStringValue();788} else {789url = String(url);790}791// We don't rely on goog.url here to prevent a dependency cycle.792const parsedScheme = goog.html.SafeUrl.extractScheme(url);793if (!goog.asserts.assert(794parsedScheme !== 'javascript:', '%s is a javascript: URL', url)) {795url = goog.html.SafeUrl.INNOCUOUS_STRING;796}797return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);798};799800/**801* Token used to ensure that object is created only from this file. No code802* outside of this file can access this token.803* @private {!Object}804* @const805*/806goog.html.SafeUrl.CONSTRUCTOR_TOKEN_PRIVATE_ = {};807808/**809* Package-internal utility method to create SafeUrl instances.810*811* @param {string} url The string to initialize the SafeUrl object with.812* @return {!goog.html.SafeUrl} The initialized SafeUrl object.813* @package814*/815goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse = function(816url) {817'use strict';818return new goog.html.SafeUrl(819url, goog.html.SafeUrl.CONSTRUCTOR_TOKEN_PRIVATE_);820};821822823/**824* `INNOCUOUS_STRING` wrapped in a `SafeUrl`.825* @const {!goog.html.SafeUrl}826*/827goog.html.SafeUrl.INNOCUOUS_URL =828goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(829goog.html.SafeUrl.INNOCUOUS_STRING);830831832/**833* A SafeUrl corresponding to the special about:blank url.834* @const {!goog.html.SafeUrl}835*/836goog.html.SafeUrl.ABOUT_BLANK =837goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(838'about:blank');839840841