8000 Add support for TrustedTypes (#5692) · Polymer/polymer@10220c9 · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
8000

Commit 10220c9

Browse files
authored
Add support for TrustedTypes (#5692)
* Add support for TrustedTypes to html-tag.js This avoids setting innerHTML to a string. * Comment on why .slice() the XMLSerializer output * Handle data binding Trusted Types into attributes Fixes #5648 * Lint clean
1 parent 1c0153d commit 10220c9

File tree

3 files changed

+75
-8
lines changed

3 files changed

+75
-8
lines changed

.eslintrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"Polymer": true,
4141
"ShadyDOM": true,
4242
"ShadyCSS": true,
43-
"JSCompiler_renameProperty": true
43+
"JSCompiler_renameProperty": true,
44+
"trustedTypes": true
4445
}
4546
}

lib/mixins/property-accessors.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ while (proto) {
2626
proto = Object.getPrototypeOf(proto);
2727
}
2828

29+
const isTrustedType = (() => {
30+
if (!window.trustedTypes) {
31+
return () => false;
32+
}
33+
return (val) => trustedTypes.isHTML(val) ||
34+
trustedTypes.isScript(val) || trustedTypes.isScriptURL(val);
35+
})();
36+
2937
/**
3038
* Used to save the value of a property that will be overridden with
3139
* an accessor. If the `model` is a prototype, the values will be saved
@@ -215,6 +223,14 @@ export const PropertyAccessors = dedupingMixin(superClass => {
215223
if (value instanceof Date) {
216224
return value.toString();
217225
} else if (value) {
226+
if (isTrustedType(value)) {
227+
/**
228+
* Here `value` isn't actually a string, but it should be
229+
* passed into APIs that normally expect a string, like
230+
* elem.setAttribute.
231+
*/
232+
return /** @type {?} */ (value);
233+
}
218234
try {
219235
return JSON.stringify(value);
220236
} catch(x) {

lib/utils/html-tag.js

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,33 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
99
*/
1010
import './boot.js';
1111

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+
1225
/**
1326
* Class representing a static string value which can be used to filter
1427
* strings by asseting that they have been created via this class. The
1528
* `value` property returns the string passed to the constructor.
1629
*/
1730
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]);
1939
/** @type {string} */
2040
this.value = string.toString();
2141
}
@@ -48,7 +68,15 @@ function literalValue(value) {
4868
*/
4969
function htmlValue(value) {
5070
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('</'));
5280
} else if (value instanceof LiteralString) {
5381
return literalValue(value);
5482
} else {
@@ -92,12 +120,35 @@ function htmlValue(value) {
92120
* @return {!HTMLTemplateElement} Constructed HTMLTemplateElement
93121
*/
94122
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;
98132
return template;
99133
};
100134

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+
101152
/**
102153
* An html literal tag that can be used with `html` to compose.
103154
* a literal string.
@@ -123,6 +174,5 @@ export const html = function html(strings, ...values) {
123174
* @return {!LiteralString} Constructed literal string
124175
*/
125176
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);
128178
};

0 commit comments

Comments
 (0)
0