diff --git a/content/themes/casper b/content/themes/casper index c4c276653d..10beda3f6c 160000 --- a/content/themes/casper +++ b/content/themes/casper @@ -1 +1 @@ -Subproject commit c4c276653dc751e50fd927bd8c88a72930f4beff +Subproject commit 10beda3f6c02921fa4644d8b33f30eefbf5af9ca diff --git a/core/client/tpl/list-item.hbs b/core/client/tpl/list-item.hbs index ae4b22dbb5..78506151f4 100644 --- a/core/client/tpl/list-item.hbs +++ b/core/client/tpl/list-item.hbs @@ -1,5 +1,5 @@ -

{{title}}

+

{{{title}}}

\ No newline at end of file diff --git a/core/server/models/base.js b/core/server/models/base.js index a8e2cd1638..65db8cae4b 100644 --- a/core/server/models/base.js +++ b/core/server/models/base.js @@ -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) { diff --git a/core/server/models/post.js b/core/server/models/post.js index 74a197c263..111c32fff9 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -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')) { diff --git a/core/server/models/settings.js b/core/server/models/settings.js index 5faf9e301a..cce389311d 100644 --- a/core/server/models/settings.js +++ b/core/server/models/settings.js @@ -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 diff --git a/core/server/models/user.js b/core/server/models/user.js index 894a3df261..d814a91dea 100644 --- a/core/server/models/user.js +++ b/core/server/models/user.js @@ -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'); }, diff --git a/core/test/unit/api_posts_spec.js b/core/test/unit/api_posts_spec.js index 6731d9222b..9355ec7f72 100644 --- a/core/test/unit/api_posts_spec.js +++ b/core/test/unit/api_posts_spec.js @@ -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': ""}).save(); + }).then(function (saved) { + saved.get('title').should.eql("</title></head><body>[removed]alert('blogtitle');[removed]"); + done(); + }).otherwise(done); + + }); + });