Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make LinkView FastBoot™-compatible #10316

Merged
merged 5 commits into from
Jan 31, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Use DOMHelper to extract URL protocol
  • Loading branch information
Tom Dale and Yehuda Katz authored and tilde-engineering committed Jan 30, 2015
commit 50bc8bedba68549b7d3c7565ca14d1d126e37276
2 changes: 1 addition & 1 deletion packages/ember-htmlbars/lib/hooks/attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default function attribute(env, morph, element, attrName, attrValue) {
if (isStream(attrValue)) {
throw new EmberError('Bound attributes are not yet supported in Ember.js');
} else {
var sanitizedValue = sanitizeAttributeValue(element, attrName, attrValue);
var sanitizedValue = sanitizeAttributeValue(env.dom, element, attrName, attrValue);
env.dom.setProperty(element, attrName, sanitizedValue);
}
}
Expand Down
23 changes: 15 additions & 8 deletions packages/ember-views/lib/system/sanitize_attribute_value.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,9 @@ export var badAttributes = {
'background': true
};

export default function sanitizeAttributeValue(element, attribute, value) {
export default function sanitizeAttributeValue(dom, element, attribute, value) {
var tagName;

if (!parsingNode) {
parsingNode = document.createElement('a');
}

if (!element) {
tagName = null;
} else {
Expand All @@ -38,9 +34,20 @@ export default function sanitizeAttributeValue(element, attribute, value) {
}

if ((tagName === null || badTags[tagName]) && badAttributes[attribute]) {
parsingNode.href = value;

if (badProtocols[parsingNode.protocol] === true) {
// Previously, we relied on creating a new `<a>` element and setting
// its `href` in order to get the DOM to parse and extract its protocol.
// Naive approaches to URL parsing are susceptible to all sorts of XSS
// attacks.
//
// However, this approach does not work in environments without a DOM,
// such as Node & FastBoot. We have extracted the logic for parsing to
// the DOM helper, so that in locations without DOM, we can substitute
// our own robust URL parsing.
//
// This will also allow us to use the new `URL` API in browsers that
// support it, and skip the process of creating an element entirely.
var protocol = dom.protocolForURL(value);
if (badProtocols[protocol] === true) {
return 'unsafe:' + value;
}
}
Expand Down
8 changes: 4 additions & 4 deletions packages/ember-views/lib/views/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -1232,7 +1232,7 @@ var View = CoreView.extend({
// Determine the current value and add it to the render buffer
// if necessary.
attributeValue = get(this, property);
View.applyAttributeBindings(buffer, attributeName, attributeValue);
View.applyAttributeBindings(this.renderer._dom, buffer, attributeName, attributeValue);
} else {
unspecifiedAttributeBindings[property] = attributeName;
}
Expand All @@ -1252,7 +1252,7 @@ var View = CoreView.extend({

attributeValue = get(this, property);

View.applyAttributeBindings(elem, attributeName, attributeValue);
View.applyAttributeBindings(this.renderer._dom, elem, attributeName, attributeValue);
};

this.registerObserver(this, property, observer);
Expand Down Expand Up @@ -2176,8 +2176,8 @@ View.views = {};
View.childViewsProperty = childViewsProperty;

// Used by Handlebars helpers, view element attributes
View.applyAttributeBindings = function(elem, name, initialValue) {
var value = sanitizeAttributeValue(elem[0], name, initialValue);
View.applyAttributeBindings = function(dom, elem, name, initialValue) {
var value = sanitizeAttributeValue(dom, elem[0], name, initialValue);
var type = typeOf(value);

// if this changes, also change the logic in ember-handlebars/lib/helpers/binding.js
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import sanitizeAttributeValue from "ember-views/system/sanitize_attribute_value";
import { SafeString } from "ember-htmlbars/utils/string";
import { DOMHelper } from "morph";

QUnit.module('ember-views: sanitizeAttributeValue(null, "href")');

var goodProtocols = ['https', 'http', 'ftp', 'tel', 'file'];
var dom = new DOMHelper();

for (var i = 0, l = goodProtocols.length; i < l; i++) {
buildProtocolTest(goodProtocols[i]);
Expand All @@ -14,7 +16,7 @@ function buildProtocolTest(protocol) {
expect(1);

var expected = protocol + '://foo.com';
var actual = sanitizeAttributeValue(null, 'href', expected);
var actual = sanitizeAttributeValue(dom, null, 'href', expected);

equal(actual, expected, 'protocol not escaped');
});
Expand All @@ -26,7 +28,7 @@ test('blocks javascript: protocol', function() {
expect(1);

var expected = 'javascript:alert("foo")';
var actual = sanitizeAttributeValue(null, 'href', expected);
var actual = sanitizeAttributeValue(dom, null, 'href', expected);

equal(actual, 'unsafe:' + expected, 'protocol escaped');
});
Expand All @@ -37,7 +39,7 @@ test('blocks blacklisted protocols', function() {
expect(1);

var expected = 'javascript:alert("foo")';
var actual = sanitizeAttributeValue(null, 'href', expected);
var actual = sanitizeAttributeValue(dom, null, 'href', expected);

equal(actual, 'unsafe:' + expected, 'protocol escaped');
});
Expand All @@ -48,7 +50,7 @@ test('does not block SafeStrings', function() {
expect(1);

var expected = 'javascript:alert("foo")';
var actual = sanitizeAttributeValue(null, 'href', new SafeString(expected));
var actual = sanitizeAttributeValue(dom, null, 'href', new SafeString(expected));

equal(actual, expected, 'protocol unescaped');
});