@@ -9,13 +9,33 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
9
9
*/
10
10
import './boot.js' ;
11
11
12
+ /**
13
+ * Our TrustedTypePolicy for HTML which is declared using the Polymer html
14
+ * template tag function.
15
+ *
16
+ * That HTML is a developer-authored constant, and is parsed with innerHTML
17
+ * before any untrusted expressions have been mixed in. Therefor it is
18
+ * considered safe by construction.
19
+ *
20
+ * @type {!TrustedTypePolicy|undefined }
21
+ */
22
+ const policy = window . trustedTypes &&
23
+ trustedTypes . createPolicy ( 'polymer-html-literal' , { createHTML : ( s ) => s } ) ;
24
+
12
25
/**
13
26
* Class representing a static string value which can be used to filter
14
27
* strings by asseting that they have been created via this class. The
15
28
* `value` property returns the string passed to the constructor.
16
29
*/
17
30
class LiteralString {<
EDBE
/span>
18
- constructor ( string ) {
31
+ /**
32
+ * @param {!ITemplateArray } strings Constant parts of tagged template literal
33
+ * @param {!Array<*> } values Variable parts of tagged template literal
34
+ */
35
+ constructor ( strings , values ) {
36
+ assertValidTemplateStringParameters ( strings , values ) ;
37
+ const string = values . reduce (
38
+ ( acc , v , idx ) => acc + literalValue ( v ) + strings [ idx + 1 ] , strings [ 0 ] ) ;
19
39
/** @type {string } */
20
40
this . value = string . toString ( ) ;
21
41
}
@@ -48,7 +68,15 @@ function literalValue(value) {
48
68
*/
49
69
function htmlValue ( value ) {
50
70
if ( value instanceof HTMLTemplateElement ) {
51
- return /** @type {!HTMLTemplateElement } */ ( value ) . innerHTML ;
71
+ // Use the XML serializer to avoid xMSS attacks from browsers' sometimes
72
+ // unexpected formatting / cleanup of innerHTML.
73
+ const serializedNewTree = new XMLSerializer ( ) . serializeToString (
74
+ /** @type {!HTMLTemplateElement } */ ( value ) ) ;
75
+ // The XMLSerializer is similar to .outerHTML, so slice off the leading
76
+ // and trailing parts of the <template> wrapper tag.
77
+ return serializedNewTree . slice (
78
+ serializedNewTree . indexOf ( '>' ) + 1 ,
79
+ serializedNewTree . lastIndexOf ( '</' ) ) ;
52
80
} else if ( value instanceof LiteralString ) {
53
81
return literalValue ( value ) ;
54
82
} else {
@@ -92,12 +120,35 @@ function htmlValue(value) {
92
120
* @return {!HTMLTemplateElement } Constructed HTMLTemplateElement
93
121
*/
94
122
export const html = function html ( strings , ...values ) {
95
- const template = /** @type {!HTMLTemplateElement } */ ( document . createElement ( 'template' ) ) ;
96
- template . innerHTML = values . reduce ( ( acc , v , idx ) =>
97
- acc + htmlValue ( v ) + strings [ idx + 1 ] , strings [ 0 ] ) ;
123
+ assertValidTemplateStringParameters ( strings , values ) ;
124
+ const template =
125
+ /** @type {!HTMLTemplateElement } */ ( document . createElement ( 'template' ) ) ;
126
+ let value = values . reduce (
127
+ ( acc , v , idx ) => acc + htmlValue ( v ) + strings [ idx + 1 ] , strings [ 0 ] ) ;
128
+ if ( policy ) {
129
+ value = policy . createHTML ( value ) ;
130
+ }
131
+ template . innerHTML = value ;
98
132
return template ;
99
133
} ;
100
134
135
+ /**
136
+ * @param {!ITemplateArray } strings Constant parts of tagged template literal
137
+ * @param {!Array<*> } values Array of values from quasis
138
+ */
139
+ const assertValidTemplateStringParameters = ( strings , values ) => {
140
+ // Note: if/when https://github.com/tc39/proposal-array-is-template-object
141
+ // is standardized, use that instead when available, as it can perform an
142
+ // unforgable check (though of course, the function itself can be forged).
143
+ if ( ! Array . isArray ( strings ) || ! Array . isArray ( strings . raw ) ||
144
+ ( values . length !== strings . length - 1 ) ) {
145
+ // This is either caused by a browser bug, a compiler bug, or someone
146
+ // calling the html template tag function as a regular function.
147
+ //
148
+ throw new TypeError ( 'Invalid call to the html template tag' ) ;
149
+ }
150
+ } ;
151
+
101
152
/**
102
153
* An html literal tag that can be used with `html` to compose.
103
154
* a literal string.
@@ -123,6 +174,5 @@ export const html = function html(strings, ...values) {
123
174
* @return {!LiteralString } Constructed literal string
124
175
*/
125
176
export const htmlLiteral = function ( strings , ...values ) {
126
- return new LiteralString ( values . reduce ( ( acc , v , idx ) =>
127
- acc + literalValue ( v ) + strings [ idx + 1 ] , strings [ 0 ] ) ) ;
177
+ return new LiteralString ( strings , values ) ;
128
178
} ;
0 commit comments