123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443 |
- // Copyright 2012 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview Soy data primitives.
- *
- * The goal is to encompass data types used by Soy, especially to mark content
- * as known to be "safe".
- *
- * @author gboyer@google.com (Garrett Boyer)
- */
- goog.provide('goog.soy.data.SanitizedContent');
- goog.provide('goog.soy.data.SanitizedContentKind');
- goog.provide('goog.soy.data.SanitizedCss');
- goog.provide('goog.soy.data.SanitizedHtml');
- goog.provide('goog.soy.data.SanitizedHtmlAttribute');
- goog.provide('goog.soy.data.SanitizedJs');
- goog.provide('goog.soy.data.SanitizedTrustedResourceUri');
- goog.provide('goog.soy.data.SanitizedUri');
- goog.provide('goog.soy.data.UnsanitizedText');
- goog.require('goog.Uri');
- goog.require('goog.html.SafeHtml');
- goog.require('goog.html.SafeScript');
- goog.require('goog.html.SafeStyle');
- goog.require('goog.html.SafeUrl');
- goog.require('goog.html.TrustedResourceUrl');
- goog.require('goog.html.uncheckedconversions');
- goog.require('goog.i18n.bidi.Dir');
- goog.require('goog.string.Const');
- /**
- * A type of textual content.
- *
- * This is an enum of type Object so that these values are unforgeable.
- *
- * @enum {!Object}
- */
- goog.soy.data.SanitizedContentKind = {
- /**
- * A snippet of HTML that does not start or end inside a tag, comment, entity,
- * or DOCTYPE; and that does not contain any executable code
- * (JS, {@code <object>}s, etc.) from a different trust domain.
- */
- HTML: goog.DEBUG ? {sanitizedContentKindHtml: true} : {},
- /**
- * Executable Javascript code or expression, safe for insertion in a
- * script-tag or event handler context, known to be free of any
- * attacker-controlled scripts. This can either be side-effect-free
- * Javascript (such as JSON) or Javascript that's entirely under Google's
- * control.
- */
- JS: goog.DEBUG ? {sanitizedContentJsChars: true} : {},
- /** A properly encoded portion of a URI. */
- URI: goog.DEBUG ? {sanitizedContentUri: true} : {},
- /** A resource URI not under attacker control. */
- TRUSTED_RESOURCE_URI:
- goog.DEBUG ? {sanitizedContentTrustedResourceUri: true} : {},
- /**
- * Repeated attribute names and values. For example,
- * {@code dir="ltr" foo="bar" onclick="trustedFunction()" checked}.
- */
- ATTRIBUTES: goog.DEBUG ? {sanitizedContentHtmlAttribute: true} : {},
- // TODO: Consider separating rules, declarations, and values into
- // separate types, but for simplicity, we'll treat explicitly blessed
- // SanitizedContent as allowed in all of these contexts.
- /**
- * A CSS3 declaration, property, value or group of semicolon separated
- * declarations.
- */
- CSS: goog.DEBUG ? {sanitizedContentCss: true} : {},
- /**
- * Unsanitized plain-text content.
- *
- * This is effectively the "null" entry of this enum, and is sometimes used
- * to explicitly mark content that should never be used unescaped. Since any
- * string is safe to use as text, being of ContentKind.TEXT makes no
- * guarantees about its safety in any other context such as HTML.
- */
- TEXT: goog.DEBUG ? {sanitizedContentKindText: true} : {}
- };
- /**
- * A string-like object that carries a content-type and a content direction.
- *
- * IMPORTANT! Do not create these directly, nor instantiate the subclasses.
- * Instead, use a trusted, centrally reviewed library as endorsed by your team
- * to generate these objects. Otherwise, you risk accidentally creating
- * SanitizedContent that is attacker-controlled and gets evaluated unescaped in
- * templates.
- *
- * @constructor
- */
- goog.soy.data.SanitizedContent = function() {
- throw Error('Do not instantiate directly');
- };
- /**
- * The context in which this content is safe from XSS attacks.
- * @type {goog.soy.data.SanitizedContentKind}
- */
- goog.soy.data.SanitizedContent.prototype.contentKind;
- /**
- * The content's direction; null if unknown and thus to be estimated when
- * necessary.
- * @type {?goog.i18n.bidi.Dir}
- */
- goog.soy.data.SanitizedContent.prototype.contentDir = null;
- /**
- * The already-safe content.
- * @protected {string}
- */
- goog.soy.data.SanitizedContent.prototype.content;
- /**
- * Gets the already-safe content.
- * @return {string}
- */
- goog.soy.data.SanitizedContent.prototype.getContent = function() {
- return this.content;
- };
- /** @override */
- goog.soy.data.SanitizedContent.prototype.toString = function() {
- return this.content;
- };
- /**
- * Converts sanitized content of kind TEXT or HTML into SafeHtml. HTML content
- * is converted without modification, while text content is HTML-escaped.
- * @return {!goog.html.SafeHtml}
- * @throws {Error} when the content kind is not TEXT or HTML.
- */
- goog.soy.data.SanitizedContent.prototype.toSafeHtml = function() {
- if (this.contentKind === goog.soy.data.SanitizedContentKind.TEXT) {
- return goog.html.SafeHtml.htmlEscape(this.toString());
- }
- if (this.contentKind !== goog.soy.data.SanitizedContentKind.HTML) {
- throw Error('Sanitized content was not of kind TEXT or HTML.');
- }
- return goog.html.uncheckedconversions
- .safeHtmlFromStringKnownToSatisfyTypeContract(
- goog.string.Const.from(
- 'Soy SanitizedContent of kind HTML produces ' +
- 'SafeHtml-contract-compliant value.'),
- this.toString(), this.contentDir);
- };
- /**
- * Converts sanitized content of kind URI into SafeUrl without modification.
- * @return {!goog.html.SafeUrl}
- * @throws {Error} when the content kind is not URI.
- */
- goog.soy.data.SanitizedContent.prototype.toSafeUrl = function() {
- if (this.contentKind !== goog.soy.data.SanitizedContentKind.URI) {
- throw Error('Sanitized content was not of kind URI.');
- }
- return goog.html.uncheckedconversions
- .safeUrlFromStringKnownToSatisfyTypeContract(
- goog.string.Const.from(
- 'Soy SanitizedContent of kind URI produces ' +
- 'SafeHtml-contract-compliant value.'),
- this.toString());
- };
- /**
- * Unsanitized plain text string.
- *
- * While all strings are effectively safe to use as a plain text, there are no
- * guarantees about safety in any other context such as HTML. This is
- * sometimes used to mark that should never be used unescaped.
- *
- * @param {*} content Plain text with no guarantees.
- * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
- * unknown and thus to be estimated when necessary. Default: null.
- * @extends {goog.soy.data.SanitizedContent}
- * @constructor
- */
- goog.soy.data.UnsanitizedText = function(content, opt_contentDir) {
- // Not calling the superclass constructor which just throws an exception.
- /** @override */
- this.content = String(content);
- this.contentDir = opt_contentDir != null ? opt_contentDir : null;
- };
- goog.inherits(goog.soy.data.UnsanitizedText, goog.soy.data.SanitizedContent);
- /** @override */
- goog.soy.data.UnsanitizedText.prototype.contentKind =
- goog.soy.data.SanitizedContentKind.TEXT;
- /**
- * Content of type {@link goog.soy.data.SanitizedContentKind.HTML}.
- *
- * The content is a string of HTML that can safely be embedded in a PCDATA
- * context in your app. If you would be surprised to find that an HTML
- * sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs) and
- * you wouldn't write a template that produces {@code s} on security or privacy
- * grounds, then don't pass {@code s} here. The default content direction is
- * unknown, i.e. to be estimated when necessary.
- *
- * @extends {goog.soy.data.SanitizedContent}
- * @constructor
- */
- goog.soy.data.SanitizedHtml = function() {
- goog.soy.data.SanitizedHtml.base(this, 'constructor');
- };
- goog.inherits(goog.soy.data.SanitizedHtml, goog.soy.data.SanitizedContent);
- /** @override */
- goog.soy.data.SanitizedHtml.prototype.contentKind =
- goog.soy.data.SanitizedContentKind.HTML;
- /**
- * Checks if the value could be used as the Soy type {html}.
- * @param {*} value
- * @return {boolean}
- */
- goog.soy.data.SanitizedHtml.isCompatibleWith = function(value) {
- return goog.isString(value) ||
- value instanceof goog.soy.data.SanitizedHtml ||
- value instanceof goog.soy.data.UnsanitizedText ||
- value instanceof goog.html.SafeHtml;
- };
- /**
- * Content of type {@link goog.soy.data.SanitizedContentKind.JS}.
- *
- * The content is JavaScript source that when evaluated does not execute any
- * attacker-controlled scripts. The content direction is LTR.
- *
- * @extends {goog.soy.data.SanitizedContent}
- * @constructor
- */
- goog.soy.data.SanitizedJs = function() {
- goog.soy.data.SanitizedJs.base(this, 'constructor');
- };
- goog.inherits(goog.soy.data.SanitizedJs, goog.soy.data.SanitizedContent);
- /** @override */
- goog.soy.data.SanitizedJs.prototype.contentKind =
- goog.soy.data.SanitizedContentKind.JS;
- /** @override */
- goog.soy.data.SanitizedJs.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
- /**
- * Checks if the value could be used as the Soy type {js}.
- * @param {*} value
- * @return {boolean}
- */
- goog.soy.data.SanitizedJs.isCompatibleWith = function(value) {
- return goog.isString(value) ||
- value instanceof goog.soy.data.SanitizedJs ||
- value instanceof goog.soy.data.UnsanitizedText ||
- value instanceof goog.html.SafeScript;
- };
- /**
- * Content of type {@link goog.soy.data.SanitizedContentKind.URI}.
- *
- * The content is a URI chunk that the caller knows is safe to emit in a
- * template. The content direction is LTR.
- *
- * @extends {goog.soy.data.SanitizedContent}
- * @constructor
- */
- goog.soy.data.SanitizedUri = function() {
- goog.soy.data.SanitizedUri.base(this, 'constructor');
- };
- goog.inherits(goog.soy.data.SanitizedUri, goog.soy.data.SanitizedContent);
- /** @override */
- goog.soy.data.SanitizedUri.prototype.contentKind =
- goog.soy.data.SanitizedContentKind.URI;
- /** @override */
- goog.soy.data.SanitizedUri.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
- /**
- * Checks if the value could be used as the Soy type {uri}.
- * @param {*} value
- * @return {boolean}
- */
- goog.soy.data.SanitizedUri.isCompatibleWith = function(value) {
- return goog.isString(value) ||
- value instanceof goog.soy.data.SanitizedUri ||
- value instanceof goog.soy.data.UnsanitizedText ||
- value instanceof goog.html.SafeUrl ||
- value instanceof goog.html.TrustedResourceUrl ||
- value instanceof goog.Uri;
- };
- /**
- * Content of type
- * {@link goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI}.
- *
- * The content is a TrustedResourceUri chunk that is not under attacker control.
- * The content direction is LTR.
- *
- * @extends {goog.soy.data.SanitizedContent}
- * @constructor
- */
- goog.soy.data.SanitizedTrustedResourceUri = function() {
- goog.soy.data.SanitizedTrustedResourceUri.base(this, 'constructor');
- };
- goog.inherits(
- goog.soy.data.SanitizedTrustedResourceUri, goog.soy.data.SanitizedContent);
- /** @override */
- goog.soy.data.SanitizedTrustedResourceUri.prototype.contentKind =
- goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI;
- /** @override */
- goog.soy.data.SanitizedTrustedResourceUri.prototype.contentDir =
- goog.i18n.bidi.Dir.LTR;
- /**
- * Checks if the value could be used as the Soy type {trusted_resource_uri}.
- * @param {*} value
- * @return {boolean}
- */
- goog.soy.data.SanitizedTrustedResourceUri.isCompatibleWith = function(value) {
- return goog.isString(value) ||
- value instanceof goog.soy.data.SanitizedTrustedResourceUri ||
- value instanceof goog.soy.data.UnsanitizedText ||
- value instanceof goog.html.TrustedResourceUrl;
- };
- /**
- * Content of type {@link goog.soy.data.SanitizedContentKind.ATTRIBUTES}.
- *
- * The content should be safely embeddable within an open tag, such as a
- * key="value" pair. The content direction is LTR.
- *
- * @extends {goog.soy.data.SanitizedContent}
- * @constructor
- */
- goog.soy.data.SanitizedHtmlAttribute = function() {
- goog.soy.data.SanitizedHtmlAttribute.base(this, 'constructor');
- };
- goog.inherits(
- goog.soy.data.SanitizedHtmlAttribute, goog.soy.data.SanitizedContent);
- /** @override */
- goog.soy.data.SanitizedHtmlAttribute.prototype.contentKind =
- goog.soy.data.SanitizedContentKind.ATTRIBUTES;
- /** @override */
- goog.soy.data.SanitizedHtmlAttribute.prototype.contentDir =
- goog.i18n.bidi.Dir.LTR;
- /**
- * Checks if the value could be used as the Soy type {attribute}.
- * @param {*} value
- * @return {boolean}
- */
- goog.soy.data.SanitizedHtmlAttribute.isCompatibleWith = function(value) {
- return goog.isString(value) ||
- value instanceof goog.soy.data.SanitizedHtmlAttribute ||
- value instanceof goog.soy.data.UnsanitizedText;
- };
- /**
- * Content of type {@link goog.soy.data.SanitizedContentKind.CSS}.
- *
- * The content is non-attacker-exploitable CSS, such as {@code color:#c3d9ff}.
- * The content direction is LTR.
- *
- * @extends {goog.soy.data.SanitizedContent}
- * @constructor
- */
- goog.soy.data.SanitizedCss = function() {
- goog.soy.data.SanitizedCss.base(this, 'constructor');
- };
- goog.inherits(goog.soy.data.SanitizedCss, goog.soy.data.SanitizedContent);
- /** @override */
- goog.soy.data.SanitizedCss.prototype.contentKind =
- goog.soy.data.SanitizedContentKind.CSS;
- /** @override */
- goog.soy.data.SanitizedCss.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
- /**
- * Checks if the value could be used as the Soy type {css}.
- * @param {*} value
- * @return {boolean}
- */
- goog.soy.data.SanitizedCss.isCompatibleWith = function(value) {
- return goog.isString(value) ||
- value instanceof goog.soy.data.SanitizedCss ||
- value instanceof goog.soy.data.UnsanitizedText ||
- value instanceof goog.html.SafeStyle;
- };
|