diff --git a/index.js b/index.js index 97002bf..eeee78f 100644 --- a/index.js +++ b/index.js @@ -192,6 +192,8 @@ Router.prototype._on = function _on (method, path, opts, handler, store) { if (isParametricNode) { let isRegexNode = false + let isParamSafe = true + let backtrack = '' const regexps = [] let lastParamStartIndex = i + 1 @@ -219,8 +221,10 @@ Router.prototype._on = function _on (method, path, opts, handler, store) { regexps.push(trimRegExpStartAndEnd(regexString)) j = endOfRegexIndex + 1 + isParamSafe = true } else { - regexps.push('(.*?)') + regexps.push(isParamSafe ? '(.*?)' : `(${backtrack}|(?:(?!${backtrack}).)*)`) + isParamSafe = false } const staticPartStartIndex = j @@ -238,7 +242,7 @@ Router.prototype._on = function _on (method, path, opts, handler, store) { if (staticPart) { staticPart = staticPart.split('::').join(':') staticPart = staticPart.split('%').join('%25') - regexps.push(escapeRegExp(staticPart)) + regexps.push(backtrack = escapeRegExp(staticPart)) } lastParamStartIndex = j + 1 @@ -335,6 +339,8 @@ Router.prototype.findRoute = function findNode (method, path, constraints = {}) if (isParametricNode) { let isRegexNode = false + let isParamSafe = true + let backtrack = '' const regexps = [] let lastParamStartIndex = i + 1 @@ -344,6 +350,7 @@ Router.prototype.findRoute = function findNode (method, path, constraints = {}) const isRegexParam = charCode === 40 const isStaticPart = charCode === 45 || charCode === 46 const isEndOfNode = charCode === 47 || j === pattern.length + if (isRegexParam || isStaticPart || isEndOfNode) { const paramName = pattern.slice(lastParamStartIndex, j) params.push(paramName) @@ -361,8 +368,10 @@ Router.prototype.findRoute = function findNode (method, path, constraints = {}) regexps.push(trimRegExpStartAndEnd(regexString)) j = endOfRegexIndex + 1 + isParamSafe = false } else { - regexps.push('(.*?)') + regexps.push(isParamSafe ? '(.*?)' : `(${backtrack}|(?:(?!${backtrack}).)*)`) + isParamSafe = false } const staticPartStartIndex = j @@ -380,7 +389,7 @@ Router.prototype.findRoute = function findNode (method, path, constraints = {}) if (staticPart) { staticPart = staticPart.split('::').join(':') staticPart = staticPart.split('%').join('%25') - regexps.push(escapeRegExp(staticPart)) + regexps.push(backtrack = escapeRegExp(staticPart)) } lastParamStartIndex = j + 1 diff --git a/test/issue-17.test.js b/test/issue-17.test.js index 7fbd3fb..55fc64e 100644 --- a/test/issue-17.test.js +++ b/test/issue-17.test.js @@ -132,8 +132,8 @@ test('Multi parametric route / 2', t => { }) findMyWay.on('GET', '/a/:p1-:p2', (req, res, params) => { - t.equal(params.p1, 'foo') - t.equal(params.p2, 'bar-baz') + t.equal(params.p1, 'foo-bar') + t.equal(params.p2, 'baz') }) findMyWay.on('GET', '/b/:p1.:p2', (req, res, params) => { diff --git a/test/optional-params.test.js b/test/optional-params.test.js index c1534ba..1665065 100644 --- a/test/optional-params.test.js +++ b/test/optional-params.test.js @@ -68,8 +68,8 @@ test('Multi parametric route with optional param', (t) => { findMyWay.on('GET', '/a/:p1-:p2?', (req, res, params) => { if (params.p1 && params.p2) { - t.equal(params.p1, 'foo') - t.equal(params.p2, 'bar-baz') + t.equal(params.p1, 'foo-bar') + t.equal(params.p2, 'baz') } }) diff --git a/test/regex.test.js b/test/regex.test.js index f17ee99..77ffc47 100644 --- a/test/regex.test.js +++ b/test/regex.test.js @@ -255,3 +255,17 @@ test('Disable safe regex check', t => { } }) }) + +test('prevent back-tracking', (t) => { + t.plan(0) + t.setTimeout(20) + + const findMyWay = FindMyWay({ + defaultRoute: () => { + t.fail('route not matched') + } + }) + + findMyWay.on('GET', '/:foo-:bar-', (req, res, params) => {}) + findMyWay.find('GET', '/' + '-'.repeat(16_000) + 'a', { host: 'fastify.io' }) +})