diff --git a/lib/tools/Config.js b/lib/tools/Config.js index a3da91c4c..0e02e5169 100644 --- a/lib/tools/Config.js +++ b/lib/tools/Config.js @@ -16,6 +16,10 @@ var Config = module.exports = { 'max' : 'The maximum of "%s" is %s, but now is %s', 'min' : 'The minimum of "%s" is %s, but now is %s' }, + // Cache for compiled regex patterns to prevent ReDoS + _regexCache: {}, + // Maximum input length for regex validation to prevent ReDoS + _maxRegexInputLength: 10000, /** * Schema definition. * @returns {exports|*} @@ -182,9 +186,27 @@ Config._valid = function(key, value, sch){ } // Verify RegExp if exists. - if (this._error(type == '[object String]' && sch.regex && !(new RegExp(sch.regex)).test(value), - 'regex', key, sch.desc || ('should match ' + sch.regex))) { - return null; + if (type == '[object String]' && sch.regex) { + // Prevent ReDoS: limit input length + if (value && value.length > Config._maxRegexInputLength) { + if (this._error(true, 'regex', key, 'Input exceeds maximum length of ' + Config._maxRegexInputLength + ' characters')) { + return null; + } + } + // Cache compiled regex to prevent ReDoS and improve performance + if (!Config._regexCache[sch.regex]) { + try { + Config._regexCache[sch.regex] = new RegExp(sch.regex); + } catch (e) { + if (this._error(true, 'regex', key, 'Invalid regex pattern: ' + e.message)) { + return null; + } + } + } + if (this._error(!Config._regexCache[sch.regex].test(value), + 'regex', key, sch.desc || ('should match ' + sch.regex))) { + return null; + } } // Verify maximum / minimum of Number value.