Path: blob/trunk/third_party/closure/goog/html/safestylesheet.js
2868 views
// Copyright 2014 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview The SafeStyleSheet type and its builders.16*17* TODO(xtof): Link to document stating type contract.18*/1920goog.provide('goog.html.SafeStyleSheet');2122goog.require('goog.array');23goog.require('goog.asserts');24goog.require('goog.string');25goog.require('goog.string.Const');26goog.require('goog.string.TypedString');27282930/**31* A string-like object which represents a CSS style sheet and that carries the32* security type contract that its value, as a string, will not cause untrusted33* script execution (XSS) when evaluated as CSS in a browser.34*35* Instances of this type must be created via the factory method36* {@code goog.html.SafeStyleSheet.fromConstant} and not by invoking its37* constructor. The constructor intentionally takes no parameters and the type38* is immutable; hence only a default instance corresponding to the empty string39* can be obtained via constructor invocation.40*41* A SafeStyleSheet's string representation can safely be interpolated as the42* content of a style element within HTML. The SafeStyleSheet string should43* not be escaped before interpolation.44*45* Values of this type must be composable, i.e. for any two values46* {@code styleSheet1} and {@code styleSheet2} of this type,47* {@code goog.html.SafeStyleSheet.unwrap(styleSheet1) +48* goog.html.SafeStyleSheet.unwrap(styleSheet2)} must itself be a value that49* satisfies the SafeStyleSheet type constraint. This requirement implies that50* for any value {@code styleSheet} of this type,51* {@code goog.html.SafeStyleSheet.unwrap(styleSheet1)} must end in52* "beginning of rule" context.5354* A SafeStyleSheet can be constructed via security-reviewed unchecked55* conversions. In this case producers of SafeStyleSheet must ensure themselves56* that the SafeStyleSheet does not contain unsafe script. Note in particular57* that {@code <} is dangerous, even when inside CSS strings, and so should58* always be forbidden or CSS-escaped in user controlled input. For example, if59* {@code </style><script>evil</script>"} were interpolated60* inside a CSS string, it would break out of the context of the original61* style element and {@code evil} would execute. Also note that within an HTML62* style (raw text) element, HTML character references, such as63* {@code &lt;}, are not allowed. See64*65http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements66* (similar considerations apply to the style element).67*68* @see goog.html.SafeStyleSheet#fromConstant69* @constructor70* @final71* @struct72* @implements {goog.string.TypedString}73*/74goog.html.SafeStyleSheet = function() {75/**76* The contained value of this SafeStyleSheet. The field has a purposely77* ugly name to make (non-compiled) code that attempts to directly access this78* field stand out.79* @private {string}80*/81this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = '';8283/**84* A type marker used to implement additional run-time type checking.85* @see goog.html.SafeStyleSheet#unwrap86* @const {!Object}87* @private88*/89this.SAFE_STYLE_SHEET_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =90goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;91};929394/**95* @override96* @const97*/98goog.html.SafeStyleSheet.prototype.implementsGoogStringTypedString = true;99100101/**102* Type marker for the SafeStyleSheet type, used to implement additional103* run-time type checking.104* @const {!Object}105* @private106*/107goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};108109110/**111* Creates a new SafeStyleSheet object by concatenating values.112* @param {...(!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>)}113* var_args Values to concatenate.114* @return {!goog.html.SafeStyleSheet}115*/116goog.html.SafeStyleSheet.concat = function(var_args) {117var result = '';118119/**120* @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}121* argument122*/123var addArgument = function(argument) {124if (goog.isArray(argument)) {125goog.array.forEach(argument, addArgument);126} else {127result += goog.html.SafeStyleSheet.unwrap(argument);128}129};130131goog.array.forEach(arguments, addArgument);132return goog.html.SafeStyleSheet133.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(result);134};135136137/**138* Creates a SafeStyleSheet object from a compile-time constant string.139*140* {@code styleSheet} must not have any < characters in it, so that141* the syntactic structure of the surrounding HTML is not affected.142*143* @param {!goog.string.Const} styleSheet A compile-time-constant string from144* which to create a SafeStyleSheet.145* @return {!goog.html.SafeStyleSheet} A SafeStyleSheet object initialized to146* {@code styleSheet}.147*/148goog.html.SafeStyleSheet.fromConstant = function(styleSheet) {149var styleSheetString = goog.string.Const.unwrap(styleSheet);150if (styleSheetString.length === 0) {151return goog.html.SafeStyleSheet.EMPTY;152}153// > is a valid character in CSS selectors and there's no strict need to154// block it if we already block <.155goog.asserts.assert(156!goog.string.contains(styleSheetString, '<'),157"Forbidden '<' character in style sheet string: " + styleSheetString);158return goog.html.SafeStyleSheet159.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheetString);160};161162163/**164* Returns this SafeStyleSheet's value as a string.165*166* IMPORTANT: In code where it is security relevant that an object's type is167* indeed {@code SafeStyleSheet}, use {@code goog.html.SafeStyleSheet.unwrap}168* instead of this method. If in doubt, assume that it's security relevant. In169* particular, note that goog.html functions which return a goog.html type do170* not guarantee the returned instance is of the right type. For example:171*172* <pre>173* var fakeSafeHtml = new String('fake');174* fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;175* var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);176* // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by177* // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml178* // instanceof goog.html.SafeHtml.179* </pre>180*181* @see goog.html.SafeStyleSheet#unwrap182* @override183*/184goog.html.SafeStyleSheet.prototype.getTypedStringValue = function() {185return this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;186};187188189if (goog.DEBUG) {190/**191* Returns a debug string-representation of this value.192*193* To obtain the actual string value wrapped in a SafeStyleSheet, use194* {@code goog.html.SafeStyleSheet.unwrap}.195*196* @see goog.html.SafeStyleSheet#unwrap197* @override198*/199goog.html.SafeStyleSheet.prototype.toString = function() {200return 'SafeStyleSheet{' +201this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ + '}';202};203}204205206/**207* Performs a runtime check that the provided object is indeed a208* SafeStyleSheet object, and returns its value.209*210* @param {!goog.html.SafeStyleSheet} safeStyleSheet The object to extract from.211* @return {string} The safeStyleSheet object's contained string, unless212* the run-time type check fails. In that case, {@code unwrap} returns an213* innocuous string, or, if assertions are enabled, throws214* {@code goog.asserts.AssertionError}.215*/216goog.html.SafeStyleSheet.unwrap = function(safeStyleSheet) {217// Perform additional Run-time type-checking to ensure that218// safeStyleSheet is indeed an instance of the expected type. This219// provides some additional protection against security bugs due to220// application code that disables type checks.221// Specifically, the following checks are performed:222// 1. The object is an instance of the expected type.223// 2. The object is not an instance of a subclass.224// 3. The object carries a type marker for the expected type. "Faking" an225// object requires a reference to the type marker, which has names intended226// to stand out in code reviews.227if (safeStyleSheet instanceof goog.html.SafeStyleSheet &&228safeStyleSheet.constructor === goog.html.SafeStyleSheet &&229safeStyleSheet230.SAFE_STYLE_SHEET_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===231goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {232return safeStyleSheet.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;233} else {234goog.asserts.fail('expected object of type SafeStyleSheet, got \'' +235safeStyleSheet + '\' of type ' + goog.typeOf(safeStyleSheet));236return 'type_error:SafeStyleSheet';237}238};239240241/**242* Package-internal utility method to create SafeStyleSheet instances.243*244* @param {string} styleSheet The string to initialize the SafeStyleSheet245* object with.246* @return {!goog.html.SafeStyleSheet} The initialized SafeStyleSheet object.247* @package248*/249goog.html.SafeStyleSheet.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse =250function(styleSheet) {251return new goog.html.SafeStyleSheet().initSecurityPrivateDoNotAccessOrElse_(252styleSheet);253};254255256/**257* Called from createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(). This258* method exists only so that the compiler can dead code eliminate static259* fields (like EMPTY) when they're not accessed.260* @param {string} styleSheet261* @return {!goog.html.SafeStyleSheet}262* @private263*/264goog.html.SafeStyleSheet.prototype.initSecurityPrivateDoNotAccessOrElse_ =265function(styleSheet) {266this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = styleSheet;267return this;268};269270271/**272* A SafeStyleSheet instance corresponding to the empty string.273* @const {!goog.html.SafeStyleSheet}274*/275goog.html.SafeStyleSheet.EMPTY =276goog.html.SafeStyleSheet277.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse('');278279280