Skip to content

Commit

Permalink
Assert that goog.dom.safe wrappers for setting href and src propertie…
Browse files Browse the repository at this point in the history
…s from

TrustedResourceUrl are correctly invoked on an object of the declared type.

Type-correct invocation of these wrappers is not security-critical, because
they require the assigned URL to have the strongest possible security contract
(TrustedResourceUrl).  However, for consistency with wrappers where
type-correct usage is security-critical (e.g. setImageSrc not being invoked
on a HTMLScriptElement), we ensure correct usage here as well.

The constraint is enforced using goog.asserts, i.e. only when assertions
are enabled. Furthermore, the assertion accepts values that are not of type
Element or Location at all, to permit the use of mock objects in tests.

Tested: Global presubmit.

RELNOTES[INC]: Assert that goog.dom.safe wrappers for setting href and src
properties from TrustedResourceUrl are correctly invoked on an object of the
declared type.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=130644937
  • Loading branch information
xtofian authored and concavelenz committed Aug 19, 2016
1 parent a5e6a3b commit 0c3a918
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 2 deletions.
8 changes: 7 additions & 1 deletion closure/goog/dom/safe.js
Expand Up @@ -194,6 +194,7 @@ goog.dom.safe.setImageSrc = function(imageElement, url) {
* @param {!goog.html.TrustedResourceUrl} url The URL to assign.
*/
goog.dom.safe.setEmbedSrc = function(embed, url) {
goog.dom.safe.assertIsHTMLEmbedElement_(embed);
embed.src = goog.html.TrustedResourceUrl.unwrap(url);
};

Expand All @@ -213,6 +214,7 @@ goog.dom.safe.setEmbedSrc = function(embed, url) {
* @param {!goog.html.TrustedResourceUrl} url The URL to assign.
*/
goog.dom.safe.setFrameSrc = function(frame, url) {
goog.dom.safe.assertIsHTMLFrameElement_(frame);
frame.src = goog.html.TrustedResourceUrl.unwrap(url);
};

Expand All @@ -232,6 +234,7 @@ goog.dom.safe.setFrameSrc = function(frame, url) {
* @param {!goog.html.TrustedResourceUrl} url The URL to assign.
*/
goog.dom.safe.setIframeSrc = function(iframe, url) {
goog.dom.safe.assertIsHTMLIFrameElement_(iframe);
iframe.src = goog.html.TrustedResourceUrl.unwrap(url);
};

Expand Down Expand Up @@ -262,6 +265,7 @@ goog.dom.safe.setIframeSrc = function(iframe, url) {
* @see goog.html.SafeUrl#sanitize
*/
goog.dom.safe.setLinkHrefAndRel = function(link, url, rel) {
goog.dom.safe.assertIsHTMLLinkElement_(link);
link.rel = rel;
if (goog.string.caseInsensitiveContains(rel, 'stylesheet')) {
goog.asserts.assert(
Expand Down Expand Up @@ -294,12 +298,13 @@ goog.dom.safe.setLinkHrefAndRel = function(link, url, rel) {
* @param {!goog.html.TrustedResourceUrl} url The URL to assign.
*/
goog.dom.safe.setObjectData = function(object, url) {
goog.dom.safe.assertIsHTMLObjectElement_(object);
object.data = goog.html.TrustedResourceUrl.unwrap(url);
};


/**
* Safely assigns a URL to an iframe element's src property.
* Safely assigns a URL to an script element's src property.
*
* Example usage:
* goog.dom.safe.setScriptSrc(scriptEl, url);
Expand All @@ -313,6 +318,7 @@ goog.dom.safe.setObjectData = function(object, url) {
* @param {!goog.html.TrustedResourceUrl} url The URL to assign.
*/
goog.dom.safe.setScriptSrc = function(script, url) {
goog.dom.safe.assertIsHTMLScriptElement_(script);
script.src = goog.html.TrustedResourceUrl.unwrap(url);
};

Expand Down
108 changes: 107 additions & 1 deletion closure/goog/dom/safe_test.js
Expand Up @@ -140,6 +140,18 @@ function testsetLinkHrefAndRel_string() {
assertEquals('about:invalid#zClosurez', mockLink.href);
}

function testsetLinkHrefAndRel_assertsType() {
if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
var otherElement = document.createElement('A');
var ex = assertThrows(function() {
goog.dom.safe.setLinkHrefAndRel(
/** @type {!HTMLLinkElement} */ (otherElement), 'http://example.com/',
'author');
});
assert(
goog.string.contains(ex.message, 'Argument is not a HTMLLinkElement'));
}
}

/**
* Returns a link element, incorrectly typed as a Location.
Expand Down Expand Up @@ -236,6 +248,101 @@ function testSetImageSrc_withHttpsUrl() {
assertEquals(safeUrl, mockImageElement.src);
}

function testSetEmbedSrc() {
var url = goog.html.TrustedResourceUrl.fromConstant(
goog.string.Const.from('javascript:trusted();'));
var mockElement = /** @type {!HTMLEmbedElement} */ ({'src': 'blarg'});
goog.dom.safe.setEmbedSrc(mockElement, url);
assertEquals('javascript:trusted();', mockElement.src);

// Asserts correct runtime type.
if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
var otherElement = document.createElement('IMAGE');
var ex = assertThrows(function() {
goog.dom.safe.setEmbedSrc(
/** @type {!HTMLEmbedElement} */ (otherElement), url);
});
assert(
goog.string.contains(ex.message, 'Argument is not a HTMLEmbedElement'));
}
}

function testSetFrameSrc() {
var url = goog.html.TrustedResourceUrl.fromConstant(
goog.string.Const.from('javascript:trusted();'));
var mockElement = /** @type {!HTMLFrameElement} */ ({'src': 'blarg'});
goog.dom.safe.setFrameSrc(mockElement, url);
assertEquals('javascript:trusted();', mockElement.src);

// Asserts correct runtime type.
if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
var otherElement = document.createElement('IMAGE');
var ex = assertThrows(function() {
goog.dom.safe.setFrameSrc(
/** @type {!HTMLFrameElement} */ (otherElement), url);
});
assert(
goog.string.contains(ex.message, 'Argument is not a HTMLFrameElement'));
}
}

function testSetIframeSrc() {
var url = goog.html.TrustedResourceUrl.fromConstant(
goog.string.Const.from('javascript:trusted();'));
var mockElement = /** @type {!HTMLIFrameElement} */ ({'src': 'blarg'});
goog.dom.safe.setIframeSrc(mockElement, url);
assertEquals('javascript:trusted();', mockElement.src);

// Asserts correct runtime type.
if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
var otherElement = document.createElement('IMAGE');
var ex = assertThrows(function() {
goog.dom.safe.setIframeSrc(
/** @type {!HTMLIFrameElement} */ (otherElement), url);
});
assert(goog.string.contains(
ex.message, 'Argument is not a HTMLIFrameElement'));
}
}

function testSetObjectData() {
var url = goog.html.TrustedResourceUrl.fromConstant(
goog.string.Const.from('javascript:trusted();'));
var mockElement = /** @type {!HTMLObjectElement} */ ({'data': 'blarg'});
goog.dom.safe.setObjectData(mockElement, url);
assertEquals('javascript:trusted();', mockElement.data);

// Asserts correct runtime type.
if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
var otherElement = document.createElement('IMAGE');
var ex = assertThrows(function() {
goog.dom.safe.setObjectData(
/** @type {!HTMLObjectElement} */ (otherElement), url);
});
assert(goog.string.contains(
ex.message, 'Argument is not a HTMLObjectElement'));
}
}

function testSetScriptSrc() {
var url = goog.html.TrustedResourceUrl.fromConstant(
goog.string.Const.from('javascript:trusted();'));
var mockElement = /** @type {!HTMLScriptElement} */ ({'src': 'blarg'});
goog.dom.safe.setScriptSrc(mockElement, url);
assertEquals('javascript:trusted();', mockElement.src);

// Asserts correct runtime type.
if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10)) {
var otherElement = document.createElement('IMAGE');
var ex = assertThrows(function() {
goog.dom.safe.setScriptSrc(
/** @type {!HTMLScriptElement} */ (otherElement), url);
});
assert(goog.string.contains(
ex.message, 'Argument is not a HTMLScriptElement'));
}
}

function testOpenInWindow() {
mockWindowOpen =
/** @type {?} */ (goog.testing.createMethodMock(window, 'open'));
Expand Down Expand Up @@ -297,7 +404,6 @@ function testAssertIsHtmlAnchorElement() {
// Ad-hoc mock objects are allowed.
var o = {foo: 'bar'};
assertNotThrows(function() { goog.dom.safe.assertIsHTMLAnchorElement_(o); });

// So are fancy mocks.
var mock = new goog.testing.StrictMock(anchorElement);
assertNotThrows(function() {
Expand Down

0 comments on commit 0c3a918

Please sign in to comment.