Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 5 additions & 2 deletions lib/application.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
(function(){
var bluebird, cursor, dom, routes, serverRendering, unescape, domUtils, span, appComponent, initAppState, observePageChange;
var bluebird, cursor, dom, routes, serverRendering, unescape, domUtils, unesc, span, appComponent, initAppState, observePageChange;
bluebird = require('bluebird');
cursor = require('./cursor');
dom = require('./dom');
routes = require('./routes');
serverRendering = require('./server-rendering');
unescape = require('lodash/string/unescape');
domUtils = require('./virtual-dom-utils');
unesc = require('lodash/string/unescape');
span = dom.span;
appComponent = React.createFactory(React.createClass({
displayName: 'arch-application',
Expand Down Expand Up @@ -61,7 +62,9 @@
path = location.pathname + location.search + location.hash;
rootDomNode = document.getElementById("application");
stateNode = document.getElementById("arch-state");
serverState = JSON.parse(unescape(stateNode.text));
serverState = JSON.parse(
unesc(
stateNode.text));
appState = serverState
? cursor(serverState)
: initAppState(app.getInitialState(), routes.resolve(routeSet, path));
Expand Down
105 changes: 6 additions & 99 deletions lib/server/render.js
Original file line number Diff line number Diff line change
@@ -1,124 +1,31 @@
(function(){
var xssFilters, jade, path, prelude, map, __template, escapeFilter;
var xssFilters, jade, path, prelude, map, __template;
xssFilters = require('xss-filters');
jade = require('jade');
path = require('path');
prelude = require('prelude-ls');
map = prelude.Obj.map;
__template = jade.compileFile(path.join(__dirname, 'index.jade'));
exports.escapeState = function(it){
return xssFilters.inHTMLData(it);
};
escapeFilter = function(k, v){
if (deepEq$(typeof v, 'string', '===')) {
return exports.escapeState(v);
} else {
return v;
}
};
exports.stringifyState = function(it){
return JSON.stringify(it, escapeFilter);
return xssFilters.inHTMLData(
JSON.stringify(
it));
};
exports.renderBody = function(meta, body, appState, options){
var stringifyState, bundlePath, archBody, layout, title;
stringifyState = exports.stringifyState;
var bundlePath, archBody, layout, title;
bundlePath = options.environment === 'development'
? "http://localhost:3001/app.js"
: "/" + options.paths['public'] + "/app.js";
archBody = __template({
'public': options.paths['public'],
bundle: bundlePath,
body: body,
state: stringifyState(appState)
state: exports.stringifyState(appState)
});
layout = meta.layout, title = meta.title;
return layout({
body: archBody,
title: title
});
};
function deepEq$(x, y, type){
var toString = {}.toString, hasOwnProperty = {}.hasOwnProperty,
has = function (obj, key) { return hasOwnProperty.call(obj, key); };
var first = true;
return eq(x, y, []);
function eq(a, b, stack) {
var className, length, size, result, alength, blength, r, key, ref, sizeB;
if (a == null || b == null) { return a === b; }
if (a.__placeholder__ || b.__placeholder__) { return true; }
if (a === b) { return a !== 0 || 1 / a == 1 / b; }
className = toString.call(a);
if (toString.call(b) != className) { return false; }
switch (className) {
case '[object String]': return a == String(b);
case '[object Number]':
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
return +a == +b;
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') { return false; }
length = stack.length;
while (length--) { if (stack[length] == a) { return true; } }
stack.push(a);
size = 0;
result = true;
if (className == '[object Array]') {
alength = a.length;
blength = b.length;
if (first) {
switch (type) {
case '===': result = alength === blength; break;
case '<==': result = alength <= blength; break;
case '<<=': result = alength < blength; break;
}
size = alength;
first = false;
} else {
result = alength === blength;
size = alength;
}
if (result) {
while (size--) {
if (!(result = size in a == size in b && eq(a[size], b[size], stack))){ break; }
}
}
} else {
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) {
return false;
}
for (key in a) {
if (has(a, key)) {
size++;
if (!(result = has(b, key) && eq(a[key], b[key], stack))) { break; }
}
}
if (result) {
sizeB = 0;
for (key in b) {
if (has(b, key)) { ++sizeB; }
}
if (first) {
if (type === '<<=') {
result = size < sizeB;
} else if (type === '<==') {
result = size <= sizeB
} else {
result = size === sizeB;
}
} else {
first = false;
result = size === sizeB;
}
}
}
stack.pop();
return result;
}
}
}).call(this);
5 changes: 1 addition & 4 deletions spec/server_spec.ls
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ describe "server" (_) ->
title: 'Hello'

spy-on render, 'stringifyState' .and.call-through!
spy-on render, 'escapeState' .and.call-through!
output = render.render-body meta, 'body', { unsafe: '</script><script>alert("xss")</script>' }, paths: { layouts: support-templates, public: 'dist' }
expect render.stringify-state .to-have-been-called!
expect render.escape-state .to-have-been-called!

it "stringifies state to JSON" ->
state = { a: true, b: false };
Expand All @@ -68,12 +66,11 @@ describe "server" (_) ->

it "escapes injected script tags" ->
state = '</script><script>alert("hi")</script>'
expect (render.escape-state state) .not.to-match /^\<\/script>/
expect (render.stringify-state state) .not.to-match /^\<\/script>/

describe "form processing" (_) ->
it "passes through to application's form-processing"

it "renders into a provided layout"

it "handles a redirect as a 302"

8 changes: 5 additions & 3 deletions src/application.ls
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
require! <[ bluebird ./cursor ./dom ./routes ./server-rendering lodash/string/unescape ]>
require! './virtual-dom-utils': 'dom-utils'
require! {
'./virtual-dom-utils': 'dom-utils'
'lodash/string/unescape': 'unesc'
}

{span} = dom

Expand Down Expand Up @@ -55,7 +58,7 @@ module.exports =
# Initialise app state

state-node = document.get-element-by-id "arch-state"
server-state = JSON.parse unescape state-node.text
server-state = state-node.text |> unesc |> JSON.parse

app-state = if server-state
cursor server-state
Expand Down Expand Up @@ -121,4 +124,3 @@ module.exports =
body = unless location then React.render-to-string root-element else null

[meta, app-state.deref!, body, location]

9 changes: 2 additions & 7 deletions src/server/render.ls
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,11 @@ map = prelude.Obj.map;

__template = jade.compile-file (path.join __dirname, 'index.jade')

exports.escape-state = -> xss-filters.inHTMLData it;

escape-filter = (k, v) -> if typeof v === 'string' then return exports.escape-state v else return v

exports.stringify-state = -> JSON.stringify it, escape-filter
exports.stringify-state = -> it |> JSON.stringify |> xss-filters.inHTMLData
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I reading it right that this is exports. just for tests? Can we not move that function, and possibly even the pipeline in application.ls (wrapped in a function) to it's own module? Something like src/state-serialisation.ls or similar.


exports.render-body = (meta, body, app-state, options) ->
stringify-state = exports.stringify-state
bundle-path = if options.environment is 'development' then "http://localhost:3001/app.js" else "/#{options.paths.public}/app.js"
arch-body = __template public: options.paths.public, bundle: bundle-path, body: body, state: stringify-state app-state
arch-body = __template public: options.paths.public, bundle: bundle-path, body: body, state: exports.stringify-state app-state

{layout, title} = meta
layout body: arch-body, title: title