diff options
author | Brian Picciano <mediocregopher@gmail.com> | 2018-11-13 00:24:09 -0500 |
---|---|---|
committer | Brian Picciano <mediocregopher@gmail.com> | 2018-11-13 00:24:09 -0500 |
commit | 2b4757367470d8e36bc00901dac567e375796ed4 (patch) | |
tree | 72368624006c21d28228a100ee88590c7bf95e58 /assets/viz/2/goog/html | |
parent | 5ed62d23b4bbbf7717de4adfa0eaf2af19365408 (diff) |
update viz 2 to use the newest version, which has some performance improvements and is easier to read the code for. also update the description
Diffstat (limited to 'assets/viz/2/goog/html')
-rw-r--r-- | assets/viz/2/goog/html/safehtml.js | 62 | ||||
-rw-r--r-- | assets/viz/2/goog/html/safescript.js | 2 | ||||
-rw-r--r-- | assets/viz/2/goog/html/safestyle.js | 181 | ||||
-rw-r--r-- | assets/viz/2/goog/html/safestylesheet.js | 68 | ||||
-rw-r--r-- | assets/viz/2/goog/html/safeurl.js | 86 | ||||
-rw-r--r-- | assets/viz/2/goog/html/trustedresourceurl.js | 174 | ||||
-rw-r--r-- | assets/viz/2/goog/html/uncheckedconversions.js | 12 |
7 files changed, 505 insertions, 80 deletions
diff --git a/assets/viz/2/goog/html/safehtml.js b/assets/viz/2/goog/html/safehtml.js index 704d826..4bffbff 100644 --- a/assets/viz/2/goog/html/safehtml.js +++ b/assets/viz/2/goog/html/safehtml.js @@ -25,6 +25,7 @@ goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.dom.TagName'); goog.require('goog.dom.tags'); +goog.require('goog.html.SafeScript'); goog.require('goog.html.SafeStyle'); goog.require('goog.html.SafeStyleSheet'); goog.require('goog.html.SafeUrl'); @@ -77,7 +78,7 @@ goog.html.SafeHtml = function() { /** * A type marker used to implement additional run-time type checking. * @see goog.html.SafeHtml#unwrap - * @const + * @const {!Object} * @private */ this.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = @@ -319,7 +320,7 @@ goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_ = goog.object.createSet( /** * @typedef {string|number|goog.string.TypedString| - * goog.html.SafeStyle.PropertyMap} + * goog.html.SafeStyle.PropertyMap|undefined} */ goog.html.SafeHtml.AttributeValue; @@ -360,8 +361,9 @@ goog.html.SafeHtml.AttributeValue; * which are not supported by this function are applet, base, embed, iframe, * link, math, object, script, style, svg, and template. * - * @param {string} tagName The name of the tag. Only tag names consisting of - * [a-zA-Z0-9-] are allowed. Tag names documented above are disallowed. + * @param {!goog.dom.TagName|string} tagName The name of the tag. Only tag names + * consisting of [a-zA-Z0-9-] are allowed. Tag names documented above are + * disallowed. * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes * Mapping from attribute names to their values. Only attribute names * consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes @@ -376,9 +378,9 @@ goog.html.SafeHtml.AttributeValue; * @throws {goog.asserts.AssertionError} If content for void tag is provided. */ goog.html.SafeHtml.create = function(tagName, opt_attributes, opt_content) { - goog.html.SafeHtml.verifyTagName(tagName); + goog.html.SafeHtml.verifyTagName(String(tagName)); return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse( - tagName, opt_attributes, opt_content); + String(tagName), opt_attributes, opt_content); }; @@ -525,6 +527,12 @@ goog.html.SafeHtml.canUseSandboxIframe = function() { * opt_attributes contains the src attribute. */ goog.html.SafeHtml.createScriptSrc = function(src, opt_attributes) { + // TODO(mlourenco): The charset attribute should probably be blocked. If + // its value is attacker controlled, the script contains attacker controlled + // sub-strings (even if properly escaped) and the server does not set charset + // then XSS is likely possible. + // https://html.spec.whatwg.org/multipage/scripting.html#dom-script-charset + // Check whether this is really TrustedResourceUrl. goog.html.TrustedResourceUrl.unwrap(src); @@ -538,6 +546,44 @@ goog.html.SafeHtml.createScriptSrc = function(src, opt_attributes) { /** + * Creates a SafeHtml representing a script tag. Does not allow the language, + * src, text or type attributes to be set. + * @param {!goog.html.SafeScript|!Array<!goog.html.SafeScript>} + * script Content to put inside the tag. Array elements are + * concatenated. + * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes + * Mapping from attribute names to their values. Only attribute names + * consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes + * the attribute to be omitted. + * @return {!goog.html.SafeHtml} The SafeHtml content with the tag. + * @throws {Error} If invalid attribute name or attribute value is provided. If + * opt_attributes contains the language, src, text or type attribute. + */ +goog.html.SafeHtml.createScript = function(script, opt_attributes) { + for (var attr in opt_attributes) { + var attrLower = attr.toLowerCase(); + if (attrLower == 'language' || attrLower == 'src' || attrLower == 'text' || + attrLower == 'type') { + throw Error('Cannot set "' + attrLower + '" attribute'); + } + } + + var content = ''; + script = goog.array.concat(script); + for (var i = 0; i < script.length; i++) { + content += goog.html.SafeScript.unwrap(script[i]); + } + // Convert to SafeHtml so that it's not HTML-escaped. This is safe because + // as part of its contract, SafeScript should have no dangerous '<'. + var htmlContent = + goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse( + content, goog.i18n.bidi.Dir.NEUTRAL); + return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse( + 'script', opt_attributes, htmlContent); +}; + + +/** * Creates a SafeHtml representing a style tag. The type attribute is set * to "text/css". * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>} @@ -636,7 +682,7 @@ goog.html.SafeHtml.getAttrNameAndValue_ = function(tagName, name, value) { throw Error( 'Attribute "' + name + '" requires goog.string.Const value, "' + value + '" given.'); - // URL attributes handled differently accroding to tag. + // URL attributes handled differently according to tag. } else if (name.toLowerCase() in goog.html.SafeHtml.URL_ATTRIBUTES_) { if (value instanceof goog.html.TrustedResourceUrl) { value = goog.html.TrustedResourceUrl.unwrap(value); @@ -881,7 +927,7 @@ goog.html.SafeHtml.stringifyAttributes = function(tagName, opt_attributes) { /** - * @param {!Object<string, string>} fixedAttributes + * @param {!Object<string, ?goog.html.SafeHtml.AttributeValue>} fixedAttributes * @param {!Object<string, string>} defaultAttributes * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes * Optional attributes passed to create*(). diff --git a/assets/viz/2/goog/html/safescript.js b/assets/viz/2/goog/html/safescript.js index 26f58f6..7a945eb 100644 --- a/assets/viz/2/goog/html/safescript.js +++ b/assets/viz/2/goog/html/safescript.js @@ -78,7 +78,7 @@ goog.html.SafeScript = function() { /** * A type marker used to implement additional run-time type checking. * @see goog.html.SafeScript#unwrap - * @const + * @const {!Object} * @private */ this.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = diff --git a/assets/viz/2/goog/html/safestyle.js b/assets/viz/2/goog/html/safestyle.js index a1f87cb..506a475 100644 --- a/assets/viz/2/goog/html/safestyle.js +++ b/assets/viz/2/goog/html/safestyle.js @@ -22,6 +22,7 @@ goog.provide('goog.html.SafeStyle'); goog.require('goog.array'); goog.require('goog.asserts'); +goog.require('goog.html.SafeUrl'); goog.require('goog.string'); goog.require('goog.string.Const'); goog.require('goog.string.TypedString'); @@ -42,19 +43,19 @@ goog.require('goog.string.TypedString'); * is immutable; hence only a default instance corresponding to the empty string * can be obtained via constructor invocation. * - * A SafeStyle's string representation ({@link #getTypedStringValue()}) can - * safely: + * SafeStyle's string representation can safely be: * <ul> - * <li>Be interpolated as the entire content of a *quoted* HTML style - * attribute, or before already existing properties. The SafeStyle string - * *must be HTML-attribute-escaped* (where " and ' are escaped) before + * <li>Interpolated as the content of a *quoted* HTML style attribute. + * However, the SafeStyle string *must be HTML-attribute-escaped* before * interpolation. - * <li>Be interpolated as the entire content of a {}-wrapped block within a - * stylesheet, or before already existing properties. The SafeStyle string - * should not be escaped before interpolation. SafeStyle's contract also - * guarantees that the string will not be able to introduce new properties - * or elide existing ones. - * <li>Be assigned to the style property of a DOM node. The SafeStyle string + * <li>Interpolated as the content of a {}-wrapped block within a stylesheet. + * '<' characters in the SafeStyle string *must be CSS-escaped* before + * interpolation. The SafeStyle string is also guaranteed not to be able + * to introduce new properties or elide existing ones. + * <li>Interpolated as the content of a {}-wrapped block within an HTML + * <style> element. '<' characters in the SafeStyle string + * *must be CSS-escaped* before interpolation. + * <li>Assigned to the style property of a DOM node. The SafeStyle string * should not be escaped before being assigned to the property. * </ul> * @@ -84,7 +85,7 @@ goog.require('goog.string.TypedString'); * appended to {@code background:url("}, the resulting string may result in * the execution of a malicious script. * - * TODO(user): Consider whether we should implement UTF-8 interchange + * TODO(mlourenco): Consider whether we should implement UTF-8 interchange * validity checks and blacklisting of newlines (including Unicode ones) and * other whitespace characters (\t, \f). Document here if so and also update * SafeStyle.fromConstant(). @@ -126,7 +127,7 @@ goog.html.SafeStyle = function() { /** * A type marker used to implement additional run-time type checking. * @see goog.html.SafeStyle#unwrap - * @const + * @const {!Object} * @private */ this.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = @@ -312,7 +313,7 @@ goog.html.SafeStyle.EMPTY = /** - * The innocuous string generated by goog.html.SafeUrl.create when passed + * The innocuous string generated by goog.html.SafeStyle.create when passed * an unsafe value. * @const {string} */ @@ -320,8 +321,20 @@ goog.html.SafeStyle.INNOCUOUS_STRING = 'zClosurez'; /** + * A single property value. + * @typedef {string|!goog.string.Const|!goog.html.SafeUrl} + */ +goog.html.SafeStyle.PropertyValue; + + +/** * Mapping of property names to their values. - * @typedef {!Object<string, goog.string.Const|string>} + * We don't support numbers even though some values might be numbers (e.g. + * line-height or 0 for any length). The reason is that most numeric values need + * units (e.g. '1px') and allowing numbers could cause users forgetting about + * them. + * @typedef {!Object<string, ?goog.html.SafeStyle.PropertyValue| + * ?Array<!goog.html.SafeStyle.PropertyValue>>} */ goog.html.SafeStyle.PropertyMap; @@ -331,9 +344,12 @@ goog.html.SafeStyle.PropertyMap; * @param {goog.html.SafeStyle.PropertyMap} map Mapping of property names to * their values, for example {'margin': '1px'}. Names must consist of * [-_a-zA-Z0-9]. Values might be strings consisting of - * [-,.'"%_!# a-zA-Z0-9], where " and ' must be properly balanced. - * Other values must be wrapped in goog.string.Const. Null value causes - * skipping the property. + * [-,.'"%_!# a-zA-Z0-9], where " and ' must be properly balanced. We also + * allow simple functions like rgb() and url() which sanitizes its contents. + * Other values must be wrapped in goog.string.Const. URLs might be passed + * as goog.html.SafeUrl which will be wrapped into url(""). We also support + * array whose elements are joined with ' '. Null value causes skipping the + * property. * @return {!goog.html.SafeStyle} * @throws {Error} If invalid name is provided. * @throws {goog.asserts.AssertionError} If invalid value is provided. With @@ -350,19 +366,11 @@ goog.html.SafeStyle.create = function(map) { if (value == null) { continue; } - if (value instanceof goog.string.Const) { - value = goog.string.Const.unwrap(value); - // These characters can be used to change context and we don't want that - // even with const values. - goog.asserts.assert(!/[{;}]/.test(value), 'Value does not allow [{;}].'); - } else if (!goog.html.SafeStyle.VALUE_RE_.test(value)) { - goog.asserts.fail( - 'String value allows only [-,."\'%_!# a-zA-Z0-9], rgb() and ' + - 'rgba(), got: ' + value); - value = goog.html.SafeStyle.INNOCUOUS_STRING; - } else if (!goog.html.SafeStyle.hasBalancedQuotes_(value)) { - goog.asserts.fail('String value requires balanced quotes, got: ' + value); - value = goog.html.SafeStyle.INNOCUOUS_STRING; + if (goog.isArray(value)) { + value = goog.array.map(value, goog.html.SafeStyle.sanitizePropertyValue_) + .join(' '); + } else { + value = goog.html.SafeStyle.sanitizePropertyValue_(value); } style += name + ':' + value + ';'; } @@ -376,6 +384,50 @@ goog.html.SafeStyle.create = function(map) { /** + * Checks and converts value to string. + * @param {!goog.html.SafeStyle.PropertyValue} value + * @return {string} + * @private + */ +goog.html.SafeStyle.sanitizePropertyValue_ = function(value) { + if (value instanceof goog.html.SafeUrl) { + var url = goog.html.SafeUrl.unwrap(value); + return 'url("' + url.replace(/</g, '%3c').replace(/[\\"]/g, '\\$&') + '")'; + } + var result = value instanceof goog.string.Const ? + goog.string.Const.unwrap(value) : + goog.html.SafeStyle.sanitizePropertyValueString_(String(value)); + // These characters can be used to change context and we don't want that even + // with const values. + goog.asserts.assert(!/[{;}]/.test(result), 'Value does not allow [{;}].'); + return result; +}; + + +/** + * Checks string value. + * @param {string} value + * @return {string} + * @private + */ +goog.html.SafeStyle.sanitizePropertyValueString_ = function(value) { + var valueWithoutFunctions = + value.replace(goog.html.SafeUrl.FUNCTIONS_RE_, '$1') + .replace(goog.html.SafeUrl.URL_RE_, 'url'); + if (!goog.html.SafeStyle.VALUE_RE_.test(valueWithoutFunctions)) { + goog.asserts.fail( + 'String value allows only ' + goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ + + ' and simple functions, got: ' + value); + return goog.html.SafeStyle.INNOCUOUS_STRING; + } else if (!goog.html.SafeStyle.hasBalancedQuotes_(value)) { + goog.asserts.fail('String value requires balanced quotes, got: ' + value); + return goog.html.SafeStyle.INNOCUOUS_STRING; + } + return goog.html.SafeStyle.sanitizeUrl_(value); +}; + + +/** * Checks that quotes (" and ') are properly balanced inside a string. Assumes * that neither escape (\) nor any other character that could result in * breaking out of a string parsing context are allowed; @@ -400,7 +452,13 @@ goog.html.SafeStyle.hasBalancedQuotes_ = function(value) { }; -// Keep in sync with the error string in create(). +/** + * Characters allowed in goog.html.SafeStyle.VALUE_RE_. + * @private {string} + */ +goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ = '[-,."\'%_!# a-zA-Z0-9]'; + + /** * Regular expression for safe values. * @@ -411,13 +469,66 @@ goog.html.SafeStyle.hasBalancedQuotes_ = function(value) { * (e.g. background-attachment or font-family) and hence could allow * multiple values to get injected, but that should pose no risk of XSS. * - * The rgb() and rgba() expression checks only for XSS safety, not for CSS - * validity. + * The expression checks only for XSS safety, not for CSS validity. * @const {!RegExp} * @private */ goog.html.SafeStyle.VALUE_RE_ = - /^([-,."'%_!# a-zA-Z0-9]+|(?:rgb|hsl)a?\([0-9.%, ]+\))$/; + new RegExp('^' + goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ + '+$'); + + +/** + * Regular expression for url(). We support URLs allowed by + * https://www.w3.org/TR/css-syntax-3/#url-token-diagram without using escape + * sequences. Use percent-encoding if you need to use special characters like + * backslash. + * @private @const {!RegExp} + */ +goog.html.SafeUrl.URL_RE_ = new RegExp( + '\\b(url\\([ \t\n]*)(' + + '\'[ -&(-\\[\\]-~]*\'' + // Printable characters except ' and \. + '|"[ !#-\\[\\]-~]*"' + // Printable characters except " and \. + '|[!#-&*-\\[\\]-~]*' + // Printable characters except [ "'()\\]. + ')([ \t\n]*\\))', + 'g'); + + +/** + * Regular expression for simple functions. + * @private @const {!RegExp} + */ +goog.html.SafeUrl.FUNCTIONS_RE_ = new RegExp( + '\\b(hsl|hsla|rgb|rgba|(rotate|scale|translate)(X|Y|Z|3d)?)' + + '\\([-0-9a-z.%, ]+\\)', + 'g'); + + +/** + * Sanitize URLs inside url(). + * + * NOTE: We could also consider using CSS.escape once that's available in the + * browsers. However, loosely matching URL e.g. with url\(.*\) and then escaping + * the contents would result in a slightly different language than CSS leading + * to confusion of users. E.g. url(")") is valid in CSS but it would be invalid + * as seen by our parser. On the other hand, url(\) is invalid in CSS but our + * parser would be fine with it. + * + * @param {string} value Untrusted CSS property value. + * @return {string} + * @private + */ +goog.html.SafeStyle.sanitizeUrl_ = function(value) { + return value.replace( + goog.html.SafeUrl.URL_RE_, function(match, before, url, after) { + var quote = ''; + url = url.replace(/^(['"])(.*)\1$/, function(match, start, inside) { + quote = start; + return inside; + }); + var sanitized = goog.html.SafeUrl.sanitize(url).getTypedStringValue(); + return before + quote + sanitized + quote + after; + }); +}; /** diff --git a/assets/viz/2/goog/html/safestylesheet.js b/assets/viz/2/goog/html/safestylesheet.js index 65a81b0..cdd9e31 100644 --- a/assets/viz/2/goog/html/safestylesheet.js +++ b/assets/viz/2/goog/html/safestylesheet.js @@ -22,6 +22,8 @@ goog.provide('goog.html.SafeStyleSheet'); goog.require('goog.array'); goog.require('goog.asserts'); +goog.require('goog.html.SafeStyle'); +goog.require('goog.object'); goog.require('goog.string'); goog.require('goog.string.Const'); goog.require('goog.string.TypedString'); @@ -84,7 +86,7 @@ goog.html.SafeStyleSheet = function() { /** * A type marker used to implement additional run-time type checking. * @see goog.html.SafeStyleSheet#unwrap - * @const + * @const {!Object} * @private */ this.SAFE_STYLE_SHEET_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = @@ -109,6 +111,70 @@ goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {}; /** + * Creates a style sheet consisting of one selector and one style definition. + * Use {@link goog.html.SafeStyleSheet.concat} to create longer style sheets. + * This function doesn't support @import, @media and similar constructs. + * @param {string} selector CSS selector, e.g. '#id' or 'tag .class, #id'. We + * support CSS3 selectors: https://w3.org/TR/css3-selectors/#selectors. + * @param {!goog.html.SafeStyle.PropertyMap|!goog.html.SafeStyle} style Style + * definition associated with the selector. + * @return {!goog.html.SafeStyleSheet} + * @throws {Error} If invalid selector is provided. + */ +goog.html.SafeStyleSheet.createRule = function(selector, style) { + if (goog.string.contains(selector, '<')) { + throw Error('Selector does not allow \'<\', got: ' + selector); + } + + // Remove strings. + var selectorToCheck = + selector.replace(/('|")((?!\1)[^\r\n\f\\]|\\[\s\S])*\1/g, ''); + + // Check characters allowed in CSS3 selectors. + if (!/^[-_a-zA-Z0-9#.:* ,>+~[\]()=^$|]+$/.test(selectorToCheck)) { + throw Error( + 'Selector allows only [-_a-zA-Z0-9#.:* ,>+~[\\]()=^$|] and ' + + 'strings, got: ' + selector); + } + + // Check balanced () and []. + if (!goog.html.SafeStyleSheet.hasBalancedBrackets_(selectorToCheck)) { + throw Error('() and [] in selector must be balanced, got: ' + selector); + } + + if (!(style instanceof goog.html.SafeStyle)) { + style = goog.html.SafeStyle.create(style); + } + var styleSheet = selector + '{' + goog.html.SafeStyle.unwrap(style) + '}'; + return goog.html.SafeStyleSheet + .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet); +}; + + +/** + * Checks if a string has balanced () and [] brackets. + * @param {string} s String to check. + * @return {boolean} + * @private + */ +goog.html.SafeStyleSheet.hasBalancedBrackets_ = function(s) { + var brackets = {'(': ')', '[': ']'}; + var expectedBrackets = []; + for (var i = 0; i < s.length; i++) { + var ch = s[i]; + if (brackets[ch]) { + expectedBrackets.push(brackets[ch]); + } else if (goog.object.contains(brackets, ch)) { + if (expectedBrackets.pop() != ch) { + return false; + } + } + } + return expectedBrackets.length == 0; +}; + + +/** * Creates a new SafeStyleSheet object by concatenating values. * @param {...(!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>)} * var_args Values to concatenate. diff --git a/assets/viz/2/goog/html/safeurl.js b/assets/viz/2/goog/html/safeurl.js index 640a070..3d1ee11 100644 --- a/assets/viz/2/goog/html/safeurl.js +++ b/assets/viz/2/goog/html/safeurl.js @@ -22,6 +22,7 @@ goog.provide('goog.html.SafeUrl'); goog.require('goog.asserts'); goog.require('goog.fs.url'); +goog.require('goog.html.TrustedResourceUrl'); goog.require('goog.i18n.bidi.Dir'); goog.require('goog.i18n.bidi.DirectionalString'); goog.require('goog.string'); @@ -38,10 +39,11 @@ goog.require('goog.string.TypedString'); * when evaluated as a hyperlink URL in a browser. * * Values of this type are guaranteed to be safe to use in URL/hyperlink - * contexts, such as, assignment to URL-valued DOM properties, or - * interpolation into a HTML template in URL context (e.g., inside a href - * attribute), in the sense that the use will not result in a - * Cross-Site-Scripting vulnerability. + * contexts, such as assignment to URL-valued DOM properties, in the sense that + * the use will not result in a Cross-Site-Scripting vulnerability. Similarly, + * SafeUrls can be interpolated into the URL context of an HTML template (e.g., + * inside a href attribute). However, appropriate HTML-escaping must still be + * applied. * * Note that, as documented in {@code goog.html.SafeUrl.unwrap}, this type's * contract does not guarantee that instances are safe to interpolate into HTML @@ -79,7 +81,7 @@ goog.html.SafeUrl = function() { /** * A type marker used to implement additional run-time type checking. * @see goog.html.SafeUrl#unwrap - * @const + * @const {!Object} * @private */ this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = @@ -241,21 +243,24 @@ goog.html.SafeUrl.fromConstant = function(url) { /** * A pattern that matches Blob or data types that can have SafeUrls created - * from URL.createObjectURL(blob) or via a data: URI. Only matches image and - * video types, currently. + * from URL.createObjectURL(blob) or via a data: URI. * @const * @private */ -goog.html.SAFE_MIME_TYPE_PATTERN_ = - /^(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm))$/i; +goog.html.SAFE_MIME_TYPE_PATTERN_ = new RegExp( + '^(?:audio/(?:3gpp|3gpp2|aac|midi|mp4|mpeg|ogg|x-m4a|x-wav|webm)|' + + 'image/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|' + + 'text/csv|' + + 'video/(?:mpeg|mp4|ogg|webm))$', + 'i'); /** * Creates a SafeUrl wrapping a blob URL for the given {@code blob}. * * The blob URL is created with {@code URL.createObjectURL}. If the MIME type - * for {@code blob} is not of a known safe image or video MIME type, then the - * SafeUrl will wrap {@link #INNOCUOUS_STRING}. + * for {@code blob} is not of a known safe audio, image or video MIME type, + * then the SafeUrl will wrap {@link #INNOCUOUS_STRING}. * * @see http://www.w3.org/TR/FileAPI/#url * @param {!Blob} blob @@ -280,10 +285,10 @@ goog.html.DATA_URL_PATTERN_ = /^data:([^;,]*);base64,[a-z0-9+\/]+=*$/i; /** * Creates a SafeUrl wrapping a data: URL, after validating it matches a - * known-safe image or video MIME type. + * known-safe audio, image or video MIME type. * * @param {string} dataUrl A valid base64 data URL with one of the whitelisted - * image or video MIME types. + * audio, image or video MIME types. * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING} * wrapped as a SafeUrl if it does not pass. */ @@ -320,6 +325,19 @@ goog.html.SafeUrl.fromTelUrl = function(telUrl) { /** + * Creates a SafeUrl from TrustedResourceUrl. This is safe because + * TrustedResourceUrl is more tightly restricted than SafeUrl. + * + * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl + * @return {!goog.html.SafeUrl} + */ +goog.html.SafeUrl.fromTrustedResourceUrl = function(trustedResourceUrl) { + return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse( + goog.html.TrustedResourceUrl.unwrap(trustedResourceUrl)); +}; + + +/** * A pattern that recognizes a commonly useful subset of URLs that satisfy * the SafeUrl contract. * @@ -336,19 +354,11 @@ goog.html.SafeUrl.fromTelUrl = function(telUrl) { * Otherwise, a colon after a double solidus (//) must be in the authority * (before port). * - * The pattern disallows &, used in HTML entity declarations before - * one of the characters in [/?#]. This disallows HTML entities used in the - * protocol name, which should never happen, e.g. "http" for "http". - * It also disallows HTML entities in the first path part of a relative path, - * e.g. "foo<bar/baz". Our existing escaping functions should not produce - * that. More importantly, it disallows masking of a colon, - * e.g. "javascript:...". - * * @private * @const {!RegExp} */ goog.html.SAFE_URL_PATTERN_ = - /^(?:(?:https?|mailto|ftp):|[^&:/?#]*(?:[/?#]|$))/i; + /^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i; /** @@ -378,6 +388,38 @@ goog.html.SafeUrl.sanitize = function(url) { return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url); }; +/** + * Creates a SafeUrl object from {@code url}. If {@code url} is a + * goog.html.SafeUrl then it is simply returned. Otherwise the input string is + * validated to match a pattern of commonly used safe URLs. + * + * {@code url} may be a URL with the http, https, mailto or ftp scheme, + * or a relative URL (i.e., a URL without a scheme; specifically, a + * scheme-relative, absolute-path-relative, or path-relative URL). + * + * This function asserts (using goog.asserts) that the URL matches this pattern. + * If it does not, in addition to failing the assert, an innocous URL will be + * returned. + * + * @see http://url.spec.whatwg.org/#concept-relative-url + * @param {string|!goog.string.TypedString} url The URL to validate. + * @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl. + */ +goog.html.SafeUrl.sanitizeAssertUnchanged = function(url) { + if (url instanceof goog.html.SafeUrl) { + return url; + } else if (url.implementsGoogStringTypedString) { + url = url.getTypedStringValue(); + } else { + url = String(url); + } + if (!goog.asserts.assert(goog.html.SAFE_URL_PATTERN_.test(url))) { + url = goog.html.SafeUrl.INNOCUOUS_STRING; + } + return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url); +}; + + /** * Type marker for the SafeUrl type, used to implement additional run-time diff --git a/assets/viz/2/goog/html/trustedresourceurl.js b/assets/viz/2/goog/html/trustedresourceurl.js index 6e3c0df..c043ef1 100644 --- a/assets/viz/2/goog/html/trustedresourceurl.js +++ b/assets/viz/2/goog/html/trustedresourceurl.js @@ -40,10 +40,11 @@ goog.require('goog.string.TypedString'); * this type. * * Instances of this type must be created via the factory method, - * ({@code goog.html.TrustedResourceUrl.fromConstant}), and not by invoking its - * constructor. The constructor intentionally takes no parameters and the type - * is immutable; hence only a default instance corresponding to the empty - * string can be obtained via constructor invocation. + * ({@code fromConstant}, {@code fromConstants}, {@code format} or {@code + * formatWithParams}), and not by invoking its constructor. The constructor + * intentionally takes no parameters and the type is immutable; hence only a + * default instance corresponding to the empty string can be obtained via + * constructor invocation. * * @see goog.html.TrustedResourceUrl#fromConstant * @constructor @@ -64,7 +65,7 @@ goog.html.TrustedResourceUrl = function() { /** * A type marker used to implement additional run-time type checking. * @see goog.html.TrustedResourceUrl#unwrap - * @const + * @const {!Object} * @private */ this.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = @@ -179,6 +180,169 @@ goog.html.TrustedResourceUrl.unwrap = function(trustedResourceUrl) { /** + * Creates a TrustedResourceUrl from a format string and arguments. + * + * The arguments for interpolation into the format string map labels to values. + * Values of type `goog.string.Const` are interpolated without modifcation. + * Values of other types are cast to string and encoded with + * encodeURIComponent. + * + * `%{<label>}` markers are used in the format string to indicate locations + * to be interpolated with the valued mapped to the given label. `<label>` + * must contain only alphanumeric and `_` characters. + * + * The format string must start with one of the following: + * - `https://<origin>/` + * - `//<origin>/` + * - `/<pathStart>` + * - `about:blank` + * + * `<origin>` must contain only alphanumeric or any of the following: `-.:[]`. + * `<pathStart>` is any character except `/` and `\`. + * + * Example usage: + * + * var url = goog.html.TrustedResourceUrl.format(goog.string.Const.from( + * 'https://www.google.com/search?q=%{query}), {'query': searchTerm}); + * + * var url = goog.html.TrustedResourceUrl.format(goog.string.Const.from( + * '//www.youtube.com/v/%{videoId}?hl=en&fs=1%{autoplay}'), { + * 'videoId': videoId, + * 'autoplay': opt_autoplay ? + * goog.string.Const.EMPTY : goog.string.Const.from('&autoplay=1') + * }); + * + * While this function can be used to create a TrustedResourceUrl from only + * constants, fromConstant() and fromConstants() are generally preferable for + * that purpose. + * + * @param {!goog.string.Const} format The format string. + * @param {!Object<string, (string|number|!goog.string.Const)>} args Mapping + * of labels to values to be interpolated into the format string. + * goog.string.Const values are interpolated without encoding. + * @return {!goog.html.TrustedResourceUrl} + * @throws {!Error} On an invalid format string or if a label used in the + * the format string is not present in args. + */ +goog.html.TrustedResourceUrl.format = function(format, args) { + var result = goog.html.TrustedResourceUrl.format_(format, args); + return goog.html.TrustedResourceUrl + .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(result); +}; + + +/** + * String version of TrustedResourceUrl.format. + * @param {!goog.string.Const} format + * @param {!Object<string, (string|number|!goog.string.Const)>} args + * @return {string} + * @throws {!Error} + * @private + */ +goog.html.TrustedResourceUrl.format_ = function(format, args) { + var formatStr = goog.string.Const.unwrap(format); + if (!goog.html.TrustedResourceUrl.BASE_URL_.test(formatStr)) { + throw new Error('Invalid TrustedResourceUrl format: ' + formatStr); + } + return formatStr.replace( + goog.html.TrustedResourceUrl.FORMAT_MARKER_, function(match, id) { + if (!Object.prototype.hasOwnProperty.call(args, id)) { + throw new Error( + 'Found marker, "' + id + '", in format string, "' + formatStr + + '", but no valid label mapping found ' + + 'in args: ' + JSON.stringify(args)); + } + var arg = args[id]; + if (arg instanceof goog.string.Const) { + return goog.string.Const.unwrap(arg); + } else { + return encodeURIComponent(String(arg)); + } + }); +}; + + +/** + * @private @const {!RegExp} + */ +goog.html.TrustedResourceUrl.FORMAT_MARKER_ = /%{(\w+)}/g; + + +/** + * The URL must be absolute, scheme-relative or path-absolute. So it must + * start with: + * - https:// followed by allowed origin characters. + * - // followed by allowed origin characters. + * - / not followed by / or \. There will only be an absolute path. + * + * Based on + * https://url.spec.whatwg.org/commit-snapshots/56b74ce7cca8883eab62e9a12666e2fac665d03d/#url-parsing + * an initial / which is not followed by another / or \ will end up in the "path + * state" and from there it can only go to "fragment state" and "query state". + * + * We don't enforce a well-formed domain name. So '.' or '1.2' are valid. + * That's ok because the origin comes from a compile-time constant. + * + * A regular expression is used instead of goog.uri for several reasons: + * - Strictness. E.g. we don't want any userinfo component and we don't + * want '/./, nor \' in the first path component. + * - Small trusted base. goog.uri is generic and might need to change, + * reasoning about all the ways it can parse a URL now and in the future + * is error-prone. + * - Code size. We expect many calls to .format(), many of which might + * not be using goog.uri. + * - Simplicity. Using goog.uri would likely not result in simpler nor shorter + * code. + * @private @const {!RegExp} + */ +goog.html.TrustedResourceUrl.BASE_URL_ = + /^(?:https:)?\/\/[0-9a-z.:[\]-]+\/|^\/[^\/\\]|^about:blank(#|$)/i; + + +/** + * Formats the URL same as TrustedResourceUrl.format and then adds extra URL + * parameters. + * + * Example usage: + * + * // Creates '//www.youtube.com/v/abc?autoplay=1' for videoId='abc' and + * // opt_autoplay=1. Creates '//www.youtube.com/v/abc' for videoId='abc' + * // and opt_autoplay=undefined. + * var url = goog.html.TrustedResourceUrl.formatWithParams( + * goog.string.Const.from('//www.youtube.com/v/%{videoId}'), + * {'videoId': videoId}, + * {'autoplay': opt_autoplay}); + * + * @param {!goog.string.Const} format The format string. + * @param {!Object<string, (string|number|!goog.string.Const)>} args Mapping + * of labels to values to be interpolated into the format string. + * goog.string.Const values are interpolated without encoding. + * @param {!Object<string, *>} params Parameters to add to URL. Parameters with + * value {@code null} or {@code undefined} are skipped. Both keys and values + * are encoded. Note that JavaScript doesn't guarantee the order of values + * in an object which might result in non-deterministic order of the + * parameters. However, browsers currently preserve the order. + * @return {!goog.html.TrustedResourceUrl} + * @throws {!Error} On an invalid format string or if a label used in the + * the format string is not present in args. + */ +goog.html.TrustedResourceUrl.formatWithParams = function(format, args, params) { + var url = goog.html.TrustedResourceUrl.format_(format, args); + var separator = /\?/.test(url) ? '&' : '?'; + for (var key in params) { + if (params[key] == null) { + continue; + } + url += separator + encodeURIComponent(key) + '=' + + encodeURIComponent(String(params[key])); + separator = '&'; + } + return goog.html.TrustedResourceUrl + .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url); +}; + + +/** * Creates a TrustedResourceUrl object from a compile-time constant string. * * Compile-time constant strings are inherently program-controlled and hence diff --git a/assets/viz/2/goog/html/uncheckedconversions.js b/assets/viz/2/goog/html/uncheckedconversions.js index 2cef322..e96697c 100644 --- a/assets/viz/2/goog/html/uncheckedconversions.js +++ b/assets/viz/2/goog/html/uncheckedconversions.js @@ -62,10 +62,6 @@ goog.require('goog.string.Const'); * unknown directionality. * @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml * object. - * @suppress {visibility} For access to SafeHtml.create... Note that this - * use is appropriate since this method is intended to be "package private" - * within goog.html. DO NOT call SafeHtml.create... from outside this - * package; use appropriate wrappers instead. */ goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract = function(justification, html, opt_dir) { @@ -104,7 +100,7 @@ goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract = goog.asserts.assertString( goog.string.Const.unwrap(justification), 'must provide justification'); goog.asserts.assert( - !goog.string.isEmpty(goog.string.Const.unwrap(justification)), + !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)), 'must provide non-empty justification'); return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse( script); @@ -116,7 +112,7 @@ goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract = * known to satisfy the SafeStyle type contract. * * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure - * that the value of {@code style} satisfies the SafeUrl type contract in all + * that the value of {@code style} satisfies the SafeStyle type contract in all * possible program states. * * @@ -146,8 +142,8 @@ goog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract = * that is known to satisfy the SafeStyleSheet type contract. * * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure - * that the value of {@code styleSheet} satisfies the SafeUrl type contract in - * all possible program states. + * that the value of {@code styleSheet} satisfies the SafeStyleSheet type + * contract in all possible program states. * * * @param {!goog.string.Const} justification A constant string explaining why |