data.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. // Copyright 2012 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Soy data primitives.
  16. *
  17. * The goal is to encompass data types used by Soy, especially to mark content
  18. * as known to be "safe".
  19. *
  20. * @author gboyer@google.com (Garrett Boyer)
  21. */
  22. goog.provide('goog.soy.data.SanitizedContent');
  23. goog.provide('goog.soy.data.SanitizedContentKind');
  24. goog.provide('goog.soy.data.SanitizedCss');
  25. goog.provide('goog.soy.data.SanitizedHtml');
  26. goog.provide('goog.soy.data.SanitizedHtmlAttribute');
  27. goog.provide('goog.soy.data.SanitizedJs');
  28. goog.provide('goog.soy.data.SanitizedTrustedResourceUri');
  29. goog.provide('goog.soy.data.SanitizedUri');
  30. goog.provide('goog.soy.data.UnsanitizedText');
  31. goog.require('goog.Uri');
  32. goog.require('goog.html.SafeHtml');
  33. goog.require('goog.html.SafeScript');
  34. goog.require('goog.html.SafeStyle');
  35. goog.require('goog.html.SafeUrl');
  36. goog.require('goog.html.TrustedResourceUrl');
  37. goog.require('goog.html.uncheckedconversions');
  38. goog.require('goog.i18n.bidi.Dir');
  39. goog.require('goog.string.Const');
  40. /**
  41. * A type of textual content.
  42. *
  43. * This is an enum of type Object so that these values are unforgeable.
  44. *
  45. * @enum {!Object}
  46. */
  47. goog.soy.data.SanitizedContentKind = {
  48. /**
  49. * A snippet of HTML that does not start or end inside a tag, comment, entity,
  50. * or DOCTYPE; and that does not contain any executable code
  51. * (JS, {@code <object>}s, etc.) from a different trust domain.
  52. */
  53. HTML: goog.DEBUG ? {sanitizedContentKindHtml: true} : {},
  54. /**
  55. * Executable Javascript code or expression, safe for insertion in a
  56. * script-tag or event handler context, known to be free of any
  57. * attacker-controlled scripts. This can either be side-effect-free
  58. * Javascript (such as JSON) or Javascript that's entirely under Google's
  59. * control.
  60. */
  61. JS: goog.DEBUG ? {sanitizedContentJsChars: true} : {},
  62. /** A properly encoded portion of a URI. */
  63. URI: goog.DEBUG ? {sanitizedContentUri: true} : {},
  64. /** A resource URI not under attacker control. */
  65. TRUSTED_RESOURCE_URI:
  66. goog.DEBUG ? {sanitizedContentTrustedResourceUri: true} : {},
  67. /**
  68. * Repeated attribute names and values. For example,
  69. * {@code dir="ltr" foo="bar" onclick="trustedFunction()" checked}.
  70. */
  71. ATTRIBUTES: goog.DEBUG ? {sanitizedContentHtmlAttribute: true} : {},
  72. // TODO: Consider separating rules, declarations, and values into
  73. // separate types, but for simplicity, we'll treat explicitly blessed
  74. // SanitizedContent as allowed in all of these contexts.
  75. /**
  76. * A CSS3 declaration, property, value or group of semicolon separated
  77. * declarations.
  78. */
  79. CSS: goog.DEBUG ? {sanitizedContentCss: true} : {},
  80. /**
  81. * Unsanitized plain-text content.
  82. *
  83. * This is effectively the "null" entry of this enum, and is sometimes used
  84. * to explicitly mark content that should never be used unescaped. Since any
  85. * string is safe to use as text, being of ContentKind.TEXT makes no
  86. * guarantees about its safety in any other context such as HTML.
  87. */
  88. TEXT: goog.DEBUG ? {sanitizedContentKindText: true} : {}
  89. };
  90. /**
  91. * A string-like object that carries a content-type and a content direction.
  92. *
  93. * IMPORTANT! Do not create these directly, nor instantiate the subclasses.
  94. * Instead, use a trusted, centrally reviewed library as endorsed by your team
  95. * to generate these objects. Otherwise, you risk accidentally creating
  96. * SanitizedContent that is attacker-controlled and gets evaluated unescaped in
  97. * templates.
  98. *
  99. * @constructor
  100. */
  101. goog.soy.data.SanitizedContent = function() {
  102. throw Error('Do not instantiate directly');
  103. };
  104. /**
  105. * The context in which this content is safe from XSS attacks.
  106. * @type {goog.soy.data.SanitizedContentKind}
  107. */
  108. goog.soy.data.SanitizedContent.prototype.contentKind;
  109. /**
  110. * The content's direction; null if unknown and thus to be estimated when
  111. * necessary.
  112. * @type {?goog.i18n.bidi.Dir}
  113. */
  114. goog.soy.data.SanitizedContent.prototype.contentDir = null;
  115. /**
  116. * The already-safe content.
  117. * @protected {string}
  118. */
  119. goog.soy.data.SanitizedContent.prototype.content;
  120. /**
  121. * Gets the already-safe content.
  122. * @return {string}
  123. */
  124. goog.soy.data.SanitizedContent.prototype.getContent = function() {
  125. return this.content;
  126. };
  127. /** @override */
  128. goog.soy.data.SanitizedContent.prototype.toString = function() {
  129. return this.content;
  130. };
  131. /**
  132. * Converts sanitized content of kind TEXT or HTML into SafeHtml. HTML content
  133. * is converted without modification, while text content is HTML-escaped.
  134. * @return {!goog.html.SafeHtml}
  135. * @throws {Error} when the content kind is not TEXT or HTML.
  136. */
  137. goog.soy.data.SanitizedContent.prototype.toSafeHtml = function() {
  138. if (this.contentKind === goog.soy.data.SanitizedContentKind.TEXT) {
  139. return goog.html.SafeHtml.htmlEscape(this.toString());
  140. }
  141. if (this.contentKind !== goog.soy.data.SanitizedContentKind.HTML) {
  142. throw Error('Sanitized content was not of kind TEXT or HTML.');
  143. }
  144. return goog.html.uncheckedconversions
  145. .safeHtmlFromStringKnownToSatisfyTypeContract(
  146. goog.string.Const.from(
  147. 'Soy SanitizedContent of kind HTML produces ' +
  148. 'SafeHtml-contract-compliant value.'),
  149. this.toString(), this.contentDir);
  150. };
  151. /**
  152. * Converts sanitized content of kind URI into SafeUrl without modification.
  153. * @return {!goog.html.SafeUrl}
  154. * @throws {Error} when the content kind is not URI.
  155. */
  156. goog.soy.data.SanitizedContent.prototype.toSafeUrl = function() {
  157. if (this.contentKind !== goog.soy.data.SanitizedContentKind.URI) {
  158. throw Error('Sanitized content was not of kind URI.');
  159. }
  160. return goog.html.uncheckedconversions
  161. .safeUrlFromStringKnownToSatisfyTypeContract(
  162. goog.string.Const.from(
  163. 'Soy SanitizedContent of kind URI produces ' +
  164. 'SafeHtml-contract-compliant value.'),
  165. this.toString());
  166. };
  167. /**
  168. * Unsanitized plain text string.
  169. *
  170. * While all strings are effectively safe to use as a plain text, there are no
  171. * guarantees about safety in any other context such as HTML. This is
  172. * sometimes used to mark that should never be used unescaped.
  173. *
  174. * @param {*} content Plain text with no guarantees.
  175. * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
  176. * unknown and thus to be estimated when necessary. Default: null.
  177. * @extends {goog.soy.data.SanitizedContent}
  178. * @constructor
  179. */
  180. goog.soy.data.UnsanitizedText = function(content, opt_contentDir) {
  181. // Not calling the superclass constructor which just throws an exception.
  182. /** @override */
  183. this.content = String(content);
  184. this.contentDir = opt_contentDir != null ? opt_contentDir : null;
  185. };
  186. goog.inherits(goog.soy.data.UnsanitizedText, goog.soy.data.SanitizedContent);
  187. /** @override */
  188. goog.soy.data.UnsanitizedText.prototype.contentKind =
  189. goog.soy.data.SanitizedContentKind.TEXT;
  190. /**
  191. * Content of type {@link goog.soy.data.SanitizedContentKind.HTML}.
  192. *
  193. * The content is a string of HTML that can safely be embedded in a PCDATA
  194. * context in your app. If you would be surprised to find that an HTML
  195. * sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs) and
  196. * you wouldn't write a template that produces {@code s} on security or privacy
  197. * grounds, then don't pass {@code s} here. The default content direction is
  198. * unknown, i.e. to be estimated when necessary.
  199. *
  200. * @extends {goog.soy.data.SanitizedContent}
  201. * @constructor
  202. */
  203. goog.soy.data.SanitizedHtml = function() {
  204. goog.soy.data.SanitizedHtml.base(this, 'constructor');
  205. };
  206. goog.inherits(goog.soy.data.SanitizedHtml, goog.soy.data.SanitizedContent);
  207. /** @override */
  208. goog.soy.data.SanitizedHtml.prototype.contentKind =
  209. goog.soy.data.SanitizedContentKind.HTML;
  210. /**
  211. * Checks if the value could be used as the Soy type {html}.
  212. * @param {*} value
  213. * @return {boolean}
  214. */
  215. goog.soy.data.SanitizedHtml.isCompatibleWith = function(value) {
  216. return goog.isString(value) ||
  217. value instanceof goog.soy.data.SanitizedHtml ||
  218. value instanceof goog.soy.data.UnsanitizedText ||
  219. value instanceof goog.html.SafeHtml;
  220. };
  221. /**
  222. * Content of type {@link goog.soy.data.SanitizedContentKind.JS}.
  223. *
  224. * The content is JavaScript source that when evaluated does not execute any
  225. * attacker-controlled scripts. The content direction is LTR.
  226. *
  227. * @extends {goog.soy.data.SanitizedContent}
  228. * @constructor
  229. */
  230. goog.soy.data.SanitizedJs = function() {
  231. goog.soy.data.SanitizedJs.base(this, 'constructor');
  232. };
  233. goog.inherits(goog.soy.data.SanitizedJs, goog.soy.data.SanitizedContent);
  234. /** @override */
  235. goog.soy.data.SanitizedJs.prototype.contentKind =
  236. goog.soy.data.SanitizedContentKind.JS;
  237. /** @override */
  238. goog.soy.data.SanitizedJs.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
  239. /**
  240. * Checks if the value could be used as the Soy type {js}.
  241. * @param {*} value
  242. * @return {boolean}
  243. */
  244. goog.soy.data.SanitizedJs.isCompatibleWith = function(value) {
  245. return goog.isString(value) ||
  246. value instanceof goog.soy.data.SanitizedJs ||
  247. value instanceof goog.soy.data.UnsanitizedText ||
  248. value instanceof goog.html.SafeScript;
  249. };
  250. /**
  251. * Content of type {@link goog.soy.data.SanitizedContentKind.URI}.
  252. *
  253. * The content is a URI chunk that the caller knows is safe to emit in a
  254. * template. The content direction is LTR.
  255. *
  256. * @extends {goog.soy.data.SanitizedContent}
  257. * @constructor
  258. */
  259. goog.soy.data.SanitizedUri = function() {
  260. goog.soy.data.SanitizedUri.base(this, 'constructor');
  261. };
  262. goog.inherits(goog.soy.data.SanitizedUri, goog.soy.data.SanitizedContent);
  263. /** @override */
  264. goog.soy.data.SanitizedUri.prototype.contentKind =
  265. goog.soy.data.SanitizedContentKind.URI;
  266. /** @override */
  267. goog.soy.data.SanitizedUri.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
  268. /**
  269. * Checks if the value could be used as the Soy type {uri}.
  270. * @param {*} value
  271. * @return {boolean}
  272. */
  273. goog.soy.data.SanitizedUri.isCompatibleWith = function(value) {
  274. return goog.isString(value) ||
  275. value instanceof goog.soy.data.SanitizedUri ||
  276. value instanceof goog.soy.data.UnsanitizedText ||
  277. value instanceof goog.html.SafeUrl ||
  278. value instanceof goog.html.TrustedResourceUrl ||
  279. value instanceof goog.Uri;
  280. };
  281. /**
  282. * Content of type
  283. * {@link goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI}.
  284. *
  285. * The content is a TrustedResourceUri chunk that is not under attacker control.
  286. * The content direction is LTR.
  287. *
  288. * @extends {goog.soy.data.SanitizedContent}
  289. * @constructor
  290. */
  291. goog.soy.data.SanitizedTrustedResourceUri = function() {
  292. goog.soy.data.SanitizedTrustedResourceUri.base(this, 'constructor');
  293. };
  294. goog.inherits(
  295. goog.soy.data.SanitizedTrustedResourceUri, goog.soy.data.SanitizedContent);
  296. /** @override */
  297. goog.soy.data.SanitizedTrustedResourceUri.prototype.contentKind =
  298. goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI;
  299. /** @override */
  300. goog.soy.data.SanitizedTrustedResourceUri.prototype.contentDir =
  301. goog.i18n.bidi.Dir.LTR;
  302. /**
  303. * Checks if the value could be used as the Soy type {trusted_resource_uri}.
  304. * @param {*} value
  305. * @return {boolean}
  306. */
  307. goog.soy.data.SanitizedTrustedResourceUri.isCompatibleWith = function(value) {
  308. return goog.isString(value) ||
  309. value instanceof goog.soy.data.SanitizedTrustedResourceUri ||
  310. value instanceof goog.soy.data.UnsanitizedText ||
  311. value instanceof goog.html.TrustedResourceUrl;
  312. };
  313. /**
  314. * Content of type {@link goog.soy.data.SanitizedContentKind.ATTRIBUTES}.
  315. *
  316. * The content should be safely embeddable within an open tag, such as a
  317. * key="value" pair. The content direction is LTR.
  318. *
  319. * @extends {goog.soy.data.SanitizedContent}
  320. * @constructor
  321. */
  322. goog.soy.data.SanitizedHtmlAttribute = function() {
  323. goog.soy.data.SanitizedHtmlAttribute.base(this, 'constructor');
  324. };
  325. goog.inherits(
  326. goog.soy.data.SanitizedHtmlAttribute, goog.soy.data.SanitizedContent);
  327. /** @override */
  328. goog.soy.data.SanitizedHtmlAttribute.prototype.contentKind =
  329. goog.soy.data.SanitizedContentKind.ATTRIBUTES;
  330. /** @override */
  331. goog.soy.data.SanitizedHtmlAttribute.prototype.contentDir =
  332. goog.i18n.bidi.Dir.LTR;
  333. /**
  334. * Checks if the value could be used as the Soy type {attribute}.
  335. * @param {*} value
  336. * @return {boolean}
  337. */
  338. goog.soy.data.SanitizedHtmlAttribute.isCompatibleWith = function(value) {
  339. return goog.isString(value) ||
  340. value instanceof goog.soy.data.SanitizedHtmlAttribute ||
  341. value instanceof goog.soy.data.UnsanitizedText;
  342. };
  343. /**
  344. * Content of type {@link goog.soy.data.SanitizedContentKind.CSS}.
  345. *
  346. * The content is non-attacker-exploitable CSS, such as {@code color:#c3d9ff}.
  347. * The content direction is LTR.
  348. *
  349. * @extends {goog.soy.data.SanitizedContent}
  350. * @constructor
  351. */
  352. goog.soy.data.SanitizedCss = function() {
  353. goog.soy.data.SanitizedCss.base(this, 'constructor');
  354. };
  355. goog.inherits(goog.soy.data.SanitizedCss, goog.soy.data.SanitizedContent);
  356. /** @override */
  357. goog.soy.data.SanitizedCss.prototype.contentKind =
  358. goog.soy.data.SanitizedContentKind.CSS;
  359. /** @override */
  360. goog.soy.data.SanitizedCss.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
  361. /**
  362. * Checks if the value could be used as the Soy type {css}.
  363. * @param {*} value
  364. * @return {boolean}
  365. */
  366. goog.soy.data.SanitizedCss.isCompatibleWith = function(value) {
  367. return goog.isString(value) ||
  368. value instanceof goog.soy.data.SanitizedCss ||
  369. value instanceof goog.soy.data.UnsanitizedText ||
  370. value instanceof goog.html.SafeStyle;
  371. };