Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.

Commit

Permalink
Add goog.html.SafeHtml.createScript() to create <script> tags.
Browse files Browse the repository at this point in the history
RELNOTES[NEW]: goog.html.SafeHtml.createScript().

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=140618170
  • Loading branch information
mlourenco authored and shicks committed Dec 1, 2016
1 parent 206b3c5 commit 073fd68
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 9 deletions.
4 changes: 2 additions & 2 deletions closure/goog/deps.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 46 additions & 1 deletion closure/goog/html/safehtml.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.dom.TagName');
goog.require('goog.dom.tags');
goog.require('goog.html.SafeScript');
goog.require('goog.html.SafeStyle');
goog.require('goog.html.SafeStyleSheet');
goog.require('goog.html.SafeUrl');
Expand Down Expand Up @@ -526,6 +527,12 @@ goog.html.SafeHtml.canUseSandboxIframe = function() {
* opt_attributes contains the src attribute.
*/
goog.html.SafeHtml.createScriptSrc = function(src, opt_attributes) {
// TODO(mlourenco): The charset attribute should probably be blocked. If
// its value is attacker controlled, the script contains attacker controlled
// sub-strings (even if properly escaped) and the server does not set charset
// then XSS is likely possible.
// https://html.spec.whatwg.org/multipage/scripting.html#dom-script-charset

// Check whether this is really TrustedResourceUrl.
goog.html.TrustedResourceUrl.unwrap(src);

Expand All @@ -538,6 +545,44 @@ goog.html.SafeHtml.createScriptSrc = function(src, opt_attributes) {
};


/**
* Creates a SafeHtml representing a script tag. Does not allow the language,
* src, text or type attributes to be set.
* @param {!goog.html.SafeScript|!Array<!goog.html.SafeScript>}
* script Content to put inside the tag. Array elements are
* concatenated.
* @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
* Mapping from attribute names to their values. Only attribute names
* consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
* the attribute to be omitted.
* @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
* @throws {Error} If invalid attribute name or attribute value is provided. If
* opt_attributes contains the language, src, text or type attribute.
*/
goog.html.SafeHtml.createScript = function(script, opt_attributes) {
for (var attr in opt_attributes) {
var attrLower = attr.toLowerCase();
if (attrLower == 'language' || attrLower == 'src' || attrLower == 'text' ||
attrLower == 'type') {
throw Error('Cannot set "' + attrLower + '" attribute');
}
}

var content = '';
script = goog.array.concat(script);
for (var i = 0; i < script.length; i++) {
content += goog.html.SafeScript.unwrap(script[i]);
}
// Convert to SafeHtml so that it's not HTML-escaped. This is safe because
// as part of its contract, SafeScript should have no dangerous '<'.
var htmlContent =
goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
content, goog.i18n.bidi.Dir.NEUTRAL);
return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
'script', opt_attributes, htmlContent);
};


/**
* Creates a SafeHtml representing a style tag. The type attribute is set
* to "text/css".
Expand Down Expand Up @@ -637,7 +682,7 @@ goog.html.SafeHtml.getAttrNameAndValue_ = function(tagName, name, value) {
throw Error(
'Attribute "' + name + '" requires goog.string.Const value, "' + value +
'" given.');
// URL attributes handled differently accroding to tag.
// URL attributes handled differently according to tag.
} else if (name.toLowerCase() in goog.html.SafeHtml.URL_ATTRIBUTES_) {
if (value instanceof goog.html.TrustedResourceUrl) {
value = goog.html.TrustedResourceUrl.unwrap(value);
Expand Down
63 changes: 57 additions & 6 deletions closure/goog/html/safehtml_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
goog.provide('goog.html.safeHtmlTest');

goog.require('goog.html.SafeHtml');
goog.require('goog.html.SafeScript');
goog.require('goog.html.SafeStyle');
goog.require('goog.html.SafeStyleSheet');
goog.require('goog.html.SafeUrl');
Expand Down Expand Up @@ -370,6 +371,54 @@ function testSafeHtmlCanUseIframeSandbox() {
}
}


function testSafeHtmlCreateScript() {
var script =
goog.html.SafeScript.fromConstant(goog.string.Const.from('function1();'));
var scriptHtml = goog.html.SafeHtml.createScript(script);
assertSameHtml('<script>function1();</script>', scriptHtml);

// Two pieces of script.
var otherScript =
goog.html.SafeScript.fromConstant(goog.string.Const.from('function2();'));
scriptHtml = goog.html.SafeHtml.createScript([script, otherScript]);
assertSameHtml('<script>function1();function2();</script>', scriptHtml);

// Set attribute.
scriptHtml = goog.html.SafeHtml.createScript(script, {'id': 'test'});
assertContains('id="test"', goog.html.SafeHtml.unwrap(scriptHtml));

// Set attribute to null.
scriptHtml =
goog.html.SafeHtml.createScript(goog.html.SafeScript.EMPTY, {'id': null});
assertSameHtml('<script></script>', scriptHtml);

// Set attribute to invalid value.
var exception = assertThrows(function() {
goog.html.SafeHtml.createScript(
goog.html.SafeScript.EMPTY, {'invalid.': 'cantdothis'});
});
assertContains('Invalid attribute name', exception.message);

// Cannot override type attribute.
exception = assertThrows(function() {
goog.html.SafeHtml.createScript(
goog.html.SafeScript.EMPTY, {'Type': 'cantdothis'});
});
assertContains('Cannot set "type"', exception.message);

// Cannot set src attribute.
exception = assertThrows(function() {
goog.html.SafeHtml.createScript(
goog.html.SafeScript.EMPTY, {'src': 'cantdothis'});
});
assertContains('Cannot set "src"', exception.message);

// Directionality.
assertEquals(goog.i18n.bidi.Dir.NEUTRAL, scriptHtml.getDirection());
}


/** @suppress {checkTypes} */
function testSafeHtmlCreateScriptSrc() {
var url = goog.html.TrustedResourceUrl.fromConstant(
Expand Down Expand Up @@ -460,25 +509,27 @@ function testSafeHtmlCreateStyle() {
// Set attribute.
styleHtml = goog.html.SafeHtml.createStyle(styleSheet, {'id': 'test'});
var styleHtmlString = goog.html.SafeHtml.unwrap(styleHtml);
assertTrue(styleHtmlString, styleHtmlString.indexOf('id="test"') != -1);
assertTrue(styleHtmlString, styleHtmlString.indexOf('type="text/css"') != -1);
assertContains('id="test"', styleHtmlString);
assertContains('type="text/css"', styleHtmlString);

// Set attribute to null.
styleHtml = goog.html.SafeHtml.createStyle(
goog.html.SafeStyleSheet.EMPTY, {'id': null});
assertSameHtml('<style type="text/css"></style>', styleHtml);

// Set attribute to invalid value.
assertThrows(function() {
styleHtml = goog.html.SafeHtml.createStyle(
var exception = assertThrows(function() {
goog.html.SafeHtml.createStyle(
goog.html.SafeStyleSheet.EMPTY, {'invalid.': 'cantdothis'});
});
assertContains('Invalid attribute name', exception.message);

// Cannot override type attribute.
assertThrows(function() {
styleHtml = goog.html.SafeHtml.createStyle(
exception = assertThrows(function() {
goog.html.SafeHtml.createStyle(
goog.html.SafeStyleSheet.EMPTY, {'Type': 'cantdothis'});
});
assertContains('Cannot override "type"', exception.message);

// Directionality.
assertEquals(goog.i18n.bidi.Dir.NEUTRAL, styleHtml.getDirection());
Expand Down

0 comments on commit 073fd68

Please sign in to comment.