fix: ignore escaped parentheses when counting capture groups - #205
fix: ignore escaped parentheses when counting capture groups#205Hashim1999164 wants to merge 1 commit into
Conversation
RegExp routes with an escaped literal paren like \( were counted as capture groups when building the params key list, so req.params used the wrong names. Skip escaped characters while scanning the source. Fixes pillarjs#204
|
I'm pretty sure this is doable with lookbehind and no other changes to logic, at least if character classes are ignored (the |
|
Good point on lookbehind. I kept the A lookbehind like Character classes like |
blakeembrey
left a comment
There was a problem hiding this comment.
Seems reasonable to me. The lookbehind might not work everywhere because you could have an escaped escape like \\\(.
But one like this should work1 (beginning of string or character other that /(?<=(?:^|[^\\])(?:\\\\)*)\((?:\?<(.*?)>)?(?!\?)/gFootnotes
|
|
And by the way escaped backslashes probably can't be found in real world paths, because most clients normalise |
|
Yeah fair point. Most clients will turn backslashes into slashes before the path even hits the router, so the weird I looked at your test commit too. Makes sense that I am fine keeping this version as is since blakeembrey already approved it and it fixes the real bug from #204. If you or the maintainers still want the lookbehind regex instead I can swap it, but I would rather not expand scope here unless someone asks for it. |
Fixes #204
The problem
When a route is a RegExp,
Layerscans the pattern source to build the list of paramkeys. The scan regexp treats every
(as the start of a capture group, including aliteral paren that has been escaped with a backslash.
So a route like:
registers three keys (
0,tenant,user) while the RegExp only has two realgroups. The names then shift by one and
req.paramscomes back as{ 0: 'acme', tenant: 'boss' }instead of{ tenant: 'acme', user: 'boss' }.The fix
Add
\\.as a leading alternative inMATCHING_GROUP_REGEXPso any escape sequenceis consumed as a single match, and skip those matches in the key-scanning loop.
I tried the shorter
(?<!\\)lookbehind first, but it is wrong for a pattern thatcontains an escaped backslash immediately followed by a real group, for example
/\\(\d+)/. The lookbehind sees the backslash before(and skips a group thatactually exists, which shifts the keys the other way and makes a working route throw.
Consuming escapes with an alternation handles that case correctly because the
\\pair is eaten before the scanner reaches the
(.Out of scope: a paren inside a character class such as
/[(]/is still miscounted.That is a separate pre-existing issue and fixing it properly needs character class
tracking, so I left it alone to keep this change small.
Test plan
test/route.jsunder the named capture group describeblock, covering a route with an escaped literal paren plus two named groups.
mainand passes with the fix.npm run lint(standard) clean.