Merge pull request #995 from ErisDS/xss

XSS
This commit is contained in:
Hannah Wolfe 2013-10-10 07:04:50 -07:00
commit 7b28056849
10 changed files with 66 additions and 11 deletions

@ -1 +1 @@
Subproject commit c4c276653dc751e50fd927bd8c88a72930f4beff
Subproject commit 10beda3f6c02921fa4644d8b33f30eefbf5af9ca

View File

@ -1,5 +1,5 @@
<a class="permalink{{#if featured}} featured{{/if}}" href="#">
<h3 class="entry-title">{{title}}</h3>
<h3 class="entry-title">{{{title}}}</h3>
<section class="entry-meta">
<time datetime="2013-01-04" class="date">
{{#if published}}

View File

@ -283,6 +283,8 @@
this.$('#entry-title').val(this.model.get('title')).focus();
this.$('#entry-markdown').text(this.model.get('markdown'));
this.listenTo(this.model, 'change:title', this.renderTitle);
this.initMarkdown();
this.renderPreview();
@ -363,6 +365,10 @@
}
},
renderTitle: function () {
this.$('#entry-title').val(this.model.get('title'));
},
// This is a hack to remove iOS6 white space on orientation change bug
// See: http://cl.ly/RGx9
orientationChange: function () {

View File

@ -18,7 +18,7 @@
this.addSubview(this.sidebar);
this.listenTo(Ghost.router, "route:settings", this.changePane);
this.listenTo(Ghost.router, 'route:settings', this.changePane);
},
changePane: function (pane) {
@ -155,7 +155,8 @@
},
saveSettings: function () {
var title = this.$('#blog-title').val(),
var self = this,
title = this.$('#blog-title').val(),
description = this.$('#blog-description').val(),
email = this.$('#email-address').val(),
postsPerPage = this.$('#postsPerPage').val();
@ -186,7 +187,7 @@
}, {
success: this.saveSuccess,
error: this.saveError
});
}).then(function () { self.render(); });
}
},
showLogo: function (e) {
@ -212,8 +213,10 @@
self.model.save(data, {
success: self.saveSuccess,
error: self.saveError
}).then(function () {
self.render();
});
self.render();
return true;
},
buttonClass: "button-save right",
@ -268,8 +271,9 @@
self.model.save(data, {
success: self.saveSuccess,
error: self.saveError
}).then(function () {
self.render();
});
self.render();
return true;
},
buttonClass: "button-save right",
@ -283,7 +287,8 @@
saveUser: function () {
var userName = this.$('#user-name').val(),
var self = this,
userName = this.$('#user-name').val(),
userEmail = this.$('#user-email').val(),
userLocation = this.$('#user-location').val(),
userWebsite = this.$('#user-website').val(),
@ -322,6 +327,8 @@
}, {
success: this.saveSuccess,
error: this.saveError
}).then(function () {
self.render();
});
}
},
@ -365,6 +372,8 @@
status: 'passive'
});
}
}).then(function () {
self.render();
});
}
},

View File

@ -1,7 +1,7 @@
<nav id="site-navigation" role="navigation">
<ul>
{{#links}}
<li class="{{#active}}current-menu-item{{/active}}"><a title="{{title}}" href="{{url}}">{{title}}</a></li>
<li class="{{#active}}current-menu-item{{/active}}"><a title="{{{title}}}" href="{{url}}">{{{title}}}</a></li>
{{/links}}
</ul>
</nav>

View File

@ -5,7 +5,8 @@ var GhostBookshelf,
_ = require('underscore'),
uuid = require('node-uuid'),
config = require('../../../config'),
Validator = require('validator').Validator;
Validator = require('validator').Validator,
sanitize = require('validator').sanitize;
// Initializes Bookshelf as its own instance, so we can modify the Models and not mess up
// others' if they're using the library outside of ghost.
@ -78,6 +79,10 @@ GhostBookshelf.Model = GhostBookshelf.Model.extend({
return attrs;
},
sanitize: function (attr) {
return sanitize(this.get(attr)).xss();
},
// #### generateSlug
// Create a string act as the permalink for an object.
generateSlug: function (Model, base) {

View File

@ -51,7 +51,7 @@ Post = GhostBookshelf.Model.extend({
this.set('html', converter.makeHtml(this.get('markdown')));
this.set('title', this.get('title').trim());
this.set('title', this.sanitize('title').trim());
if (this.hasChanged('status') && this.get('status') === 'published') {
if (!this.get('published_at')) {

View File

@ -73,7 +73,19 @@ Settings = GhostBookshelf.Model.extend({
validation[validationName].apply(validation, validationOptions);
}, this);
}
},
saving: function () {
// All blog setting keys that need their values to be escaped.
if (this.get('type') === 'blog' && _.contains(['title', 'description', 'email'], this.get('key'))) {
this.set('value', this.sanitize('value'));
}
return GhostBookshelf.Model.prototype.saving.apply(this, arguments);
}
}, {
read: function (_key) {
// Allow for just passing the key instead of attributes

View File

@ -55,6 +55,17 @@ User = GhostBookshelf.Model.extend({
}
},
saving: function () {
this.set('name', this.sanitize('name'));
this.set('email', this.sanitize('email'));
this.set('location', this.sanitize('location'));
this.set('website', this.sanitize('website'));
this.set('bio', this.sanitize('bio'));
return GhostBookshelf.Model.prototype.saving.apply(this, arguments);
},
posts: function () {
return this.hasMany(Posts, 'created_by');
},

View File

@ -367,4 +367,16 @@ describe('Post Model', function () {
done();
}).then(null, done);
});
it('should santize the title', function (done) {
new PostModel().fetch().then(function (model) {
return model.set({'title': "</title></head><body><script>alert('blogtitle');</script>"}).save();
}).then(function (saved) {
saved.get('title').should.eql("&lt;/title&gt;&lt;/head>&lt;body&gt;[removed]alert&#40;'blogtitle'&#41;;[removed]");
done();
}).otherwise(done);
});
});