From 14f6e74bad6df3f3a130aa79bc322f4798c61127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 6 Jun 2018 13:11:43 -0400 Subject: [PATCH] closes #6556 --- .../en-GB/admin/settings/advanced.json | 2 + src/middleware/headers.js | 20 +++++++ src/views/admin/settings/advanced.tpl | 7 +++ test/meta.js | 60 +++++++++++++++++++ 4 files changed, 89 insertions(+) diff --git a/public/language/en-GB/admin/settings/advanced.json b/public/language/en-GB/admin/settings/advanced.json index 8da7b1c46a..16eae5a8bd 100644 --- a/public/language/en-GB/admin/settings/advanced.json +++ b/public/language/en-GB/admin/settings/advanced.json @@ -6,7 +6,9 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", + "headers.acao-regex": "Access-Control-Allow-Origin Regular Expression", "headers.acao-help": "To deny access to all sites, leave empty", + "headers.acao-regex-help": "Enter regular expressions here to match dynamic origins. To deny access to all sites, leave empty", "headers.acac": "Access-Control-Allow-Credentials", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", diff --git a/src/middleware/headers.js b/src/middleware/headers.js index 60af68a894..f700ac4017 100644 --- a/src/middleware/headers.js +++ b/src/middleware/headers.js @@ -1,6 +1,7 @@ 'use strict'; var os = require('os'); +var winston = require('winston'); var meta = require('../meta'); @@ -24,6 +25,25 @@ module.exports = function (middleware) { } } + if (meta.config['access-control-allow-origin-regex']) { + var originsRegex = meta.config['access-control-allow-origin-regex'].split(','); + originsRegex = originsRegex.map(function (origin) { + try { + origin = new RegExp(origin.trim()); + } catch (err) { + winston.error('[middleware.addHeaders] Invalid RegExp For access-control-allow-origin ' + origin); + origin = null; + } + return origin; + }); + + originsRegex.forEach(function (regex) { + if (regex && regex.test(req.get('origin'))) { + headers['Access-Control-Allow-Origin'] = encodeURI(req.get('origin')); + } + }); + } + if (meta.config['access-control-allow-credentials']) { headers['Access-Control-Allow-Credentials'] = meta.config['access-control-allow-credentials']; } diff --git a/src/views/admin/settings/advanced.tpl b/src/views/admin/settings/advanced.tpl index b2721ff0bd..958ae73c3f 100644 --- a/src/views/admin/settings/advanced.tpl +++ b/src/views/admin/settings/advanced.tpl @@ -40,6 +40,13 @@ [[admin/settings/advanced:headers.acao-help]]

+
+ +
+

+ [[admin/settings/advanced:headers.acao-regex-help]] +

+

diff --git a/test/meta.js b/test/meta.js index 008be48975..5fe417ca2f 100644 --- a/test/meta.js +++ b/test/meta.js @@ -356,5 +356,65 @@ describe('meta', function () { done(err); }); }); + + it('should set proper Access-Control-Allow-Origin header', function (done) { + var jar = request.jar(); + var oldValue = meta.config['access-control-allow-origin-regex']; + meta.config['access-control-allow-origin-regex'] = 'match\\.this\\..+\\.domain.com, mydomain\\.com'; + request.get(nconf.get('url') + '/api/search?term=bug', { + form: { + }, + json: true, + jar: jar, + headers: { + origin: 'match.this.anything123.domain.com', + }, + }, function (err, response, body) { + assert.ifError(err); + assert.equal(response.headers['access-control-allow-origin'], 'match.this.anything123.domain.com'); + meta.config['access-control-allow-origin-regex'] = oldValue; + done(err); + }); + }); + + it('Access-Control-Allow-Origin header should be empty if origin does not match', function (done) { + var jar = request.jar(); + var oldValue = meta.config['access-control-allow-origin-regex']; + meta.config['access-control-allow-origin-regex'] = 'match\\.this\\..+\\.domain.com, mydomain\\.com'; + request.get(nconf.get('url') + '/api/search?term=bug', { + form: { + }, + json: true, + jar: jar, + headers: { + origin: 'notallowed.com', + }, + }, function (err, response, body) { + assert.ifError(err); + assert.equal(response.headers['access-control-allow-origin'], undefined); + meta.config['access-control-allow-origin-regex'] = oldValue; + done(err); + }); + }); + + it('should not error with invalid regexp', function (done) { + var jar = request.jar(); + var oldValue = meta.config['access-control-allow-origin-regex']; + meta.config['access-control-allow-origin-regex'] = '[match\\.this\\..+\\.domain.com, mydomain\\.com'; + request.get(nconf.get('url') + '/api/search?term=bug', { + form: { + }, + json: true, + jar: jar, + headers: { + origin: 'mydomain.com', + }, + }, function (err, response, body) { + assert.ifError(err); + assert.equal(response.headers['access-control-allow-origin'], 'mydomain.com'); + meta.config['access-control-allow-origin-regex'] = oldValue; + done(err); + }); + }); }); });