diff --git a/core/server/config/defaults.json b/core/server/config/defaults.json index 35d7789bbf..8f25c25399 100644 --- a/core/server/config/defaults.json +++ b/core/server/config/defaults.json @@ -94,5 +94,6 @@ }, "compress": true, "preloadHeaders": false, + "adminFrameProtection": true, "sendWelcomeEmail": true } diff --git a/core/server/web/admin/controller.js b/core/server/web/admin/controller.js index b05f1f4990..b13c47f09b 100644 --- a/core/server/web/admin/controller.js +++ b/core/server/web/admin/controller.js @@ -23,6 +23,11 @@ module.exports = function adminController(req, res) { const defaultTemplate = config.get('env') === 'production' ? 'default-prod.html' : 'default.html'; const templatePath = path.resolve(config.get('paths').adminViews, defaultTemplate); + const headers = {}; - res.sendFile(templatePath); + if (config.get('adminFrameProtection')) { + headers['X-Frame-Options'] = 'sameorigin'; + } + + res.sendFile(templatePath, {headers}); }; diff --git a/core/test/unit/web/admin/controller_spec.js b/core/test/unit/web/admin/controller_spec.js new file mode 100644 index 0000000000..ca3af3ef5b --- /dev/null +++ b/core/test/unit/web/admin/controller_spec.js @@ -0,0 +1,45 @@ +require('should'); +const sinon = require('sinon'); +const configUtils = require('../../../utils/configUtils'); +const controller = require('../../../../server/web/admin/controller'); + +describe('Admin App', function () { + describe('controller', function () { + const req = {}; + let res; + + beforeEach(function () { + res = { + sendFile: sinon.spy() + }; + + configUtils.restore(); + }); + + afterEach(function () { + sinon.restore(); + }); + + it('adds x-frame-options header when adminFrameProtection is enabled (default)', function () { + // default config: configUtils.set('adminFrameProtection', true); + controller(req, res); + + res.sendFile.called.should.be.true(); + res.sendFile.calledWith( + sinon.match.string, + sinon.match.hasNested('headers.X-Frame-Options', sinon.match('sameorigin')) + ).should.be.true(); + }); + + it('doesn\'t add x-frame-options header when adminFrameProtection is disabled', function () { + configUtils.set('adminFrameProtection', false); + controller(req, res); + + res.sendFile.called.should.be.true(); + res.sendFile.calledWith( + sinon.match.string, + sinon.match.hasNested('headers.X-Frame-Options') + ).should.be.false(); + }); + }); +});