mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 11:35:55 +01:00
Merge remote-tracking branch 'origin/develop'
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user