Skip to content

Commit

Permalink
fix(url sanitization): Pass an A markupElementRenderer when necessary…
Browse files Browse the repository at this point in the history
… (in Fastboot) (#42)

* Pass an A markupElementRenderer when necessary (in Fastboot)

Fixes #38

* run fastboot tests on travis
  • Loading branch information
bantic authored Mar 6, 2017
1 parent 908485f commit 29be168
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 3 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ env:
- EMBER_TRY_SCENARIO=ember-release
- EMBER_TRY_SCENARIO=ember-beta
- EMBER_TRY_SCENARIO=ember-canary
- EMBER_TRY_SCENARIO=fastboot-addon-tests

matrix:
fast_finish: true
Expand Down
6 changes: 6 additions & 0 deletions addon/components/render-mobiledoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { RENDER_TYPE } from 'ember-mobiledoc-dom-renderer';
import layout from '../templates/components/render-mobiledoc';
import { getDocument } from '../utils/document';
import assign from '../utils/polyfilled-assign';
import createMarkupElementRenderer from '../utils/create-markup-element-renderer';

const {
assert,
Expand Down Expand Up @@ -123,6 +124,11 @@ export default Ember.Component.extend({
let cardOptions = this.get('_cardOptions');
options.cardOptions = passedOptions ? assign(passedOptions, cardOptions) : cardOptions;

options.markupElementRenderer = assign(
createMarkupElementRenderer(this),
options.markupElementRenderer
);

let renderer = new Renderer(options);
let { result, teardown } = renderer.render(mobiledoc);

Expand Down
64 changes: 64 additions & 0 deletions addon/utils/create-markup-element-renderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* global module */
import Ember from 'ember';

const isNode = typeof window === 'undefined' && (typeof module === 'object' && typeof module.require === 'function');
const hasDOM = typeof document === 'object';
const needsMarkupElementRenderer = !isNode && !hasDOM;

function fallbackProtocolParser(str) {
let colonIdx = str.indexOf(':');
if (colonIdx === -1) { return; }

return str.replace(/^\s+/,'').split(':')[0] + ':';
}

function getProtocolForURLfn(component) {
let glimmerEnv = Ember.getOwner(component).lookup('service:-glimmer-environment');
if (glimmerEnv && glimmerEnv.protocolForURL) {
return glimmerEnv.protocolForURL;
} else {
return fallbackProtocolParser;
}
}

function createHrefSanitizer(component) {
let protocolForUrl = getProtocolForURLfn(component);
const badProtocols = [
'vbscript:', // jshint ignore:line
'javascript:' // jshint ignore:line
];

return (href) => {
let protocol = protocolForUrl(href);
if (protocol && badProtocols.indexOf(protocol) !== -1) {
return `unsafe:${href}`;
} else {
return href;
}
};
}

export default function createMarkupElementRenderer(component) {
if (!needsMarkupElementRenderer) {
return {};
} else {
let sanitizeHref = createHrefSanitizer(component);

return {
A(tagName, dom, attrs) {
let el = dom.createElement(tagName);
Object.keys(attrs).forEach(attrName => {
let attrValue = attrs[attrName];

if (attrName === 'href') {
attrValue = sanitizeHref(attrValue);
}

el.setAttribute(attrName, attrValue);
});

return el;
}
};
}
}
1 change: 0 additions & 1 deletion addon/utils/polyfilled-assign.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@ const { merge, assign } = Ember;
let polyfilledAssign = assign || merge;

export default polyfilledAssign;

7 changes: 7 additions & 0 deletions config/ember-try.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ module.exports = {
'ember-source': null
}
}
},
{
name: 'fastboot-addon-tests',
command: 'ember fastboot:test',
bower: {
dependencies: {}
}
}
]
};
28 changes: 28 additions & 0 deletions fastboot-tests/fixtures/fastboot/app/controllers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Ember from 'ember';
import {
createSimpleMobiledoc,
createMobiledocWithMarkup
} from '../utils/mobiledoc';

const mobiledocs = [
{
name: 'simple',
mobiledoc: createSimpleMobiledoc('hello world')
},
{
name: 'with-markup',
mobiledoc: createMobiledocWithMarkup('markup text', ['em'])
},
{
name: 'with-link',
mobiledoc: createMobiledocWithMarkup('linked', ['a', ['href', 'http://example.com/with-link']])
},
{
name: 'with-unsafe-link',
mobiledoc: createMobiledocWithMarkup('linked unsafe', ['a', ['href', 'javascript:evil']])
}
];

export default Ember.Controller.extend({
mobiledocs
});
12 changes: 12 additions & 0 deletions fastboot-tests/fixtures/fastboot/app/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Ember from 'ember';
import config from './config/environment';

const Router = Ember.Router.extend({
location: config.locationType,
rootURL: config.rootURL
});

Router.map(function() {
});

export default Router;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{outlet}}
9 changes: 9 additions & 0 deletions fastboot-tests/fixtures/fastboot/app/templates/index.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<h1>ember-fastboot-addon-tests</h1>

<div id="mobiledocs">
{{#each mobiledocs as |mobiledocInfo|}}
<div class="render-mobiledoc-wrapper {{mobiledocInfo.name}}">
{{render-mobiledoc mobiledoc=mobiledocInfo.mobiledoc}}
</div>
{{/each}}
</div>
79 changes: 79 additions & 0 deletions fastboot-tests/fixtures/fastboot/app/utils/mobiledoc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const MOBILEDOC_VERSION = '0.3.1';

export function createSimpleMobiledoc(text) {
return {
version: MOBILEDOC_VERSION,
markups: [],
atoms: [],
cards: [],
sections: [
[1, 'P', [
[0, [], 0, text]
]]
]
};
}

export function createMobiledocWithStrongMarkup(text) {
return {
version: MOBILEDOC_VERSION,
markups: [
['STRONG']
],
atoms: [],
cards: [],
sections: [
[1, 'P', [
[0, [0], 1, text]
]]
]
};
}

export function createMobiledocWithMarkup(text, markup) {
return {
version: MOBILEDOC_VERSION,
markups: [
markup
],
atoms: [],
cards: [],
sections: [
[1, 'P', [
[0, [0], 1, text]
]]
]
};
}

export function createMobiledocWithAtom(atomName) {
return {
version: MOBILEDOC_VERSION,
markups: [],
atoms: [
[atomName, 'value', {foo: 'bar'}]
],
cards: [],
sections: [
[1, 'P', [
[1, [], 0, 0]
]]
]
};
}

export function createMobiledocWithCard(cardName) {
return {
version: MOBILEDOC_VERSION,
markups: [],
atoms: [],
cards: [
[cardName, {foo: 'bar'}]
],
sections: [
[10, 0]
]
};
}


87 changes: 87 additions & 0 deletions fastboot-tests/index-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use strict';

const expect = require('chai').expect;
const setupTest = require('ember-fastboot-addon-tests').setupTest;

describe('index', function() {
setupTest('fastboot'/*, options */);

it('renders', function() {
return this.visit('/')
.then(function(res) {
let $ = res.jQuery;
let response = res.response;

expect(response.statusCode).to.equal(200);
expect($('body').length).to.equal(1);
expect($('h1').text().trim()).to.equal('ember-fastboot-addon-tests');
});
});

it('renders simple mobiledoc', function() {
let name = 'simple';

return this.visit('/')
.then(function(res) {
let $ = res.jQuery;
let response = res.response;

let wrapper = $(`.render-mobiledoc-wrapper.${name}`);
expect(wrapper.length).to.equal(1);

let rendered = $(`.render-mobiledoc-wrapper.${name} p`).text().trim();
expect(rendered).to.equal('hello world');
});
});

it('renders mobiledoc with markup', function() {
let name = 'with-markup';
return this.visit('/')
.then(function(res) {
let $ = res.jQuery;
let response = res.response;

let wrapper = $(`.render-mobiledoc-wrapper.${name}`);
expect(wrapper.length).to.equal(1);

let markup = $(`.render-mobiledoc-wrapper.${name} em`);
expect(markup.length).to.equal(1);
expect(markup.text().trim()).to.equal('markup text');
});
});

it('renders mobiledoc with link', function() {
let name = 'with-link';
return this.visit('/')
.then(function(res) {
let $ = res.jQuery;
let response = res.response;

let wrapper = $(`.render-mobiledoc-wrapper.${name}`);
expect(wrapper.length).to.equal(1);

let link = $(`.render-mobiledoc-wrapper.${name} a`);
expect(link.length).to.equal(1);
expect(link.attr('href')).to.equal('http://example.com/with-link');
expect(link.text().trim()).to.equal('linked');
});
});

it('renders mobiledoc with unsafe link', function() {
let name = 'with-unsafe-link';
return this.visit('/')
.then(function(res) {
let $ = res.jQuery;
let response = res.response;

let wrapper = $(`.render-mobiledoc-wrapper.${name}`);
expect(wrapper.length).to.equal(1);

let link = $(`.render-mobiledoc-wrapper.${name} a`);
expect(link.length).to.equal(1);
expect(link.attr('href')).to.equal('unsafe:javascript:evil');
expect(link.text().trim()).to.equal('linked unsafe');
});
});

});
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@
"ember-cli-htmlbars": "^1.1.1",
"ember-getowner-polyfill": "^1.2.2",
"ember-wormhole": "^0.5.1",
"mobiledoc-dom-renderer": "^0.6.3"
"mobiledoc-dom-renderer": "^0.6.4"
},
"devDependencies": {
"broccoli-asset-rev": "^2.4.2",
"chai": "^3.5.0",
"conventional-changelog": "^1.1.0",
"conventional-changelog-cli": "^1.1.1",
"ember-cli": "2.11.1",
Expand All @@ -49,9 +50,10 @@
"ember-disable-prototype-extensions": "^1.1.0",
"ember-disable-proxy-controllers": "^1.0.1",
"ember-export-application-global": "^1.0.5",
"ember-fastboot-addon-tests": "0.2.2",
"ember-load-initializers": "^0.6.0",
"ember-source": "~2.11.0",
"ember-resolver": "^2.0.3",
"ember-source": "~2.11.0",
"loader.js": "^4.0.10"
},
"engines": {
Expand Down

0 comments on commit 29be168

Please sign in to comment.