Merge remote-tracking branch 'origin/develop'

v1.18.x
Julian Lam 8 years ago
commit 6852c92ed6

@ -439,6 +439,9 @@ app.cacheBuster = null;
.replace('{pageTitle}', function () { return title; }) .replace('{pageTitle}', function () { return title; })
.replace('{browserTitle}', function () { return config.browserTitle; }); .replace('{browserTitle}', function () { return config.browserTitle; });
// Allow translation strings in title on ajaxify (#5927)
title = translator.unescape(title);
translator.translate(title, function (translated) { translator.translate(title, function (translated) {
titleObj.titles[0] = translated; titleObj.titles[0] = translated;
app.alternatingTitle(''); app.alternatingTitle('');

@ -3,6 +3,7 @@
var nconf = require('nconf'); var nconf = require('nconf');
var winston = require('winston'); var winston = require('winston');
var validator = require('validator'); var validator = require('validator');
var plugins = require('../plugins');
exports.handleURIErrors = function (err, req, res, next) { exports.handleURIErrors = function (err, req, res, next) {
// Handle cases where malformed URIs are passed in // Handle cases where malformed URIs are passed in
@ -35,30 +36,50 @@ exports.handleURIErrors = function (err, req, res, next) {
// this needs to have four arguments or express treats it as `(req, res, next)` // this needs to have four arguments or express treats it as `(req, res, next)`
// don't remove `next`! // don't remove `next`!
exports.handleErrors = function (err, req, res, next) { // eslint-disable-line no-unused-vars exports.handleErrors = function (err, req, res, next) { // eslint-disable-line no-unused-vars
switch (err.code) { var cases = {
case 'EBADCSRFTOKEN': EBADCSRFTOKEN: function () {
winston.error(req.path + '\n', err.message); winston.error(req.path + '\n', err.message);
return res.sendStatus(403); res.sendStatus(403);
case 'blacklisted-ip': },
return res.status(403).type('text/plain').send(err.message); 'blacklisted-ip': function () {
} res.status(403).type('text/plain').send(err.message);
},
};
var defaultHandler = function () {
// Display NodeBB error page
var status = parseInt(err.status, 10);
if ((status === 302 || status === 308) && err.path) {
return res.locals.isAPI ? res.set('X-Redirect', err.path).status(200).json(err.path) : res.redirect(err.path);
}
var status = parseInt(err.status, 10); winston.error(req.path + '\n', err.stack);
if ((status === 302 || status === 308) && err.path) {
return res.locals.isAPI ? res.set('X-Redirect', err.path).status(200).json(err.path) : res.redirect(err.path);
}
winston.error(req.path + '\n', err.stack); res.status(status || 500);
res.status(status || 500); var path = String(req.path || '');
if (res.locals.isAPI) {
res.json({ path: validator.escape(path), error: err.message });
} else {
var middleware = require('../middleware');
middleware.buildHeader(req, res, function () {
res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) });
});
}
};
var path = String(req.path || ''); plugins.fireHook('filter:error.handle', {
if (res.locals.isAPI) { cases: cases,
res.json({ path: validator.escape(path), error: err.message }); }, function (_err, data) {
} else { if (_err) {
var middleware = require('../middleware'); // Assume defaults
middleware.buildHeader(req, res, function () { winston.warn('[errors/handle] Unable to retrieve plugin handlers for errors: ' + _err.message);
res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) }); data.cases = cases;
}); }
}
if (data.cases.hasOwnProperty(err.code)) {
data.cases[err.code](err, req, res, defaultHandler);
} else {
defaultHandler();
}
});
}; };

@ -20,7 +20,7 @@ Blacklist.load = function (callback) {
Blacklist.get, Blacklist.get,
Blacklist.validate, Blacklist.validate,
function (rules, next) { function (rules, next) {
winston.verbose('[meta/blacklist] Loading ' + rules.valid.length + ' blacklist rules'); winston.verbose('[meta/blacklist] Loading ' + rules.valid.length + ' blacklist rule(s)' + (rules.duplicateCount > 0 ? ', ignored ' + rules.duplicateCount + ' duplicate(s)' : ''));
if (rules.invalid.length) { if (rules.invalid.length) {
winston.warn('[meta/blacklist] ' + rules.invalid.length + ' invalid blacklist rule(s) were ignored.'); winston.warn('[meta/blacklist] ' + rules.invalid.length + ' invalid blacklist rule(s) were ignored.');
} }
@ -44,8 +44,8 @@ Blacklist.save = function (rules, callback) {
db.setObject('ip-blacklist-rules', { rules: rules }, next); db.setObject('ip-blacklist-rules', { rules: rules }, next);
}, },
function (next) { function (next) {
Blacklist.load(next);
pubsub.publish('blacklist:reload'); pubsub.publish('blacklist:reload');
setImmediate(next);
}, },
], callback); ], callback);
}; };
@ -111,6 +111,7 @@ Blacklist.validate = function (rules, callback) {
var ipv6 = []; var ipv6 = [];
var cidr = []; var cidr = [];
var invalid = []; var invalid = [];
var duplicateCount = 0;
var inlineCommentMatch = /#.*$/; var inlineCommentMatch = /#.*$/;
var whitelist = ['127.0.0.1', '::1', '::ffff:0:127.0.0.1']; var whitelist = ['127.0.0.1', '::1', '::ffff:0:127.0.0.1'];
@ -122,6 +123,16 @@ Blacklist.validate = function (rules, callback) {
return rule.length && !rule.startsWith('#') ? rule : null; return rule.length && !rule.startsWith('#') ? rule : null;
}).filter(Boolean); }).filter(Boolean);
// Filter out duplicates
rules = rules.filter(function (rule, index) {
const pass = rules.indexOf(rule) === index;
if (!pass) {
duplicateCount += 1;
}
return pass;
});
// Filter out invalid rules // Filter out invalid rules
rules = rules.filter(function (rule) { rules = rules.filter(function (rule) {
var addr; var addr;
@ -167,6 +178,7 @@ Blacklist.validate = function (rules, callback) {
cidr: cidr, cidr: cidr,
valid: rules, valid: rules,
invalid: invalid, invalid: invalid,
duplicateCount: duplicateCount,
}); });
}; };

@ -9,6 +9,7 @@ var nconf = require('nconf');
var ensureLoggedIn = require('connect-ensure-login'); var ensureLoggedIn = require('connect-ensure-login');
var toobusy = require('toobusy-js'); var toobusy = require('toobusy-js');
var Benchpress = require('benchpressjs'); var Benchpress = require('benchpressjs');
var LRU = require('lru-cache');
var plugins = require('../plugins'); var plugins = require('../plugins');
var meta = require('../meta'); var meta = require('../meta');
@ -23,6 +24,10 @@ var controllers = {
helpers: require('../controllers/helpers'), helpers: require('../controllers/helpers'),
}; };
var delayCache = LRU({
maxAge: 1000 * 60,
});
var middleware = module.exports; var middleware = module.exports;
middleware.applyCSRF = csrf(); middleware.applyCSRF = csrf();
@ -186,6 +191,14 @@ middleware.processTimeagoLocales = function (req, res, next) {
middleware.delayLoading = function (req, res, next) { middleware.delayLoading = function (req, res, next) {
// Introduces an artificial delay during load so that brute force attacks are effectively mitigated // Introduces an artificial delay during load so that brute force attacks are effectively mitigated
// Add IP to cache so if too many requests are made, subsequent requests are blocked for a minute
var timesSeen = delayCache.get(req.ip) || 0;
if (timesSeen > 10) {
return res.sendStatus(429);
}
delayCache.set(req.ip, timesSeen += 1);
setTimeout(next, 1000); setTimeout(next, 1000);
}; };

@ -1,12 +1,14 @@
<script> <script>
define('/assets/templates/500.js', function () { window.addEventListener('load', function () {
function compiled(helpers, context, get, iter, helper) { define('/assets/templates/500.js', function () {
return '<div class="alert alert-danger">\n\t<strong>[[global:500.title]]</strong>\n\t<p>[[global:500.message]]</p>\n\t<p>' + function compiled(helpers, context, get, iter, helper) {
helpers.__escape(get(context && context['path'])) + '</p>\n\t' + return '<div class="alert alert-danger">\n\t<strong>[[global:500.title]]</strong>\n\t<p>[[global:500.message]]</p>\n\t<p>' +
(get(context && context['error']) ? '<p>' + helpers.__escape(get(context && context['error'])) + '</p>' : '') + '\n\n\t' + helpers.__escape(get(context && context['path'])) + '</p>\n\t' +
(get(context && context['returnLink']) ? '\n\t<p>[[error:goback]]</p>\n\t' : '') + '\n</div>\n'; (get(context && context['error']) ? '<p>' + helpers.__escape(get(context && context['error'])) + '</p>' : '') + '\n\n\t' +
} (get(context && context['returnLink']) ? '\n\t<p>[[error:goback]]</p>\n\t' : '') + '\n</div>\n';
}
return compiled; return compiled;
}); });
});
</script> </script>

@ -180,8 +180,15 @@ function setupExpressApp(app, callback) {
setupAutoLocale(app, callback); setupAutoLocale(app, callback);
} }
function ping(req, res) { function ping(req, res, next) {
res.status(200).send(req.path === '/sping' ? 'healthy' : '200'); async.waterfall([
function (next) {
db.getObject('config', next);
},
function () {
res.status(200).send(req.path === '/sping' ? 'healthy' : '200');
},
], next);
} }
function setupFavicon(app) { function setupFavicon(app) {

Loading…
Cancel
Save