fix: enforce trailing slash in mount path for .use() layers#189
Open
DukeDeSouth wants to merge 1 commit intopillarjs:masterfrom
Open
fix: enforce trailing slash in mount path for .use() layers#189DukeDeSouth wants to merge 1 commit intopillarjs:masterfrom
DukeDeSouth wants to merge 1 commit intopillarjs:masterfrom
Conversation
When a middleware is mounted with a trailing slash (e.g. `.use('/api/', handler)`),
requests without the trailing slash (e.g. `GET /api`) should not be dispatched to
the handler. Previously, `loosen()` stripped the trailing slash from the mount path
for matching purposes, which caused requests without the trailing slash to match
and receive an artificial `'/'` as `req.url`, breaking strict routing in sub-routers.
This fix adds a check in `trimPrefix()` that compares the request path against the
original (un-loosened) mount path. When the original mount path ends with a trailing
slash that was stripped by `loosen()`, the request must also include that trailing
slash to proceed. Otherwise, the layer is skipped.
Fixes: expressjs/express#2281
Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Human View
Problem
When a middleware is mounted with a trailing slash (e.g.
.use('/api/', handler)), requests without the trailing slash (e.g.GET /api) incorrectly match and are dispatched to the handler. This breaks strict routing in sub-routers becausereq.urlis set to'/'regardless of whether the original request had a trailing slash.This is a well-known 11-year-old bug confirmed by @dougwilson.
Root Cause
loosen()strips trailing slashes from mount paths for non-strict matching. This is correct for sub-path matching (e.g./api/usersshould match mount/api/), but it also allows/api(without trailing slash) to match mount/api/. When this happens,trimPrefix()setsreq.urlto'/'(because the empty string is replaced with/), and the sub-router cannot distinguish between requests that originally had a trailing slash and those that didn't.Fix
Store the original (un-loosened) mount path in
Layer.originalPath. IntrimPrefix(), after existing path validation, check if the original mount path ended with a trailing slash that was stripped byloosen(). If so, verify the request path also includes that trailing slash. If not, skip the layer.The check is:
layer.originalPath === layerPath + '/'(was trailing slash stripped?) AND!path.startsWith(layer.originalPath)(does request include it?).Changes
lib/layer.js: Addthis.originalPath = pathto preserve original mount path (+1 line)index.js: Add path boundary validation intrimPrefix()(+12 lines)test/router.js: Update 1 existing test, add 9 new tests for comprehensive coverage (+201 lines)Test Results
Before/After
GET /api/GET /apiGET /api/usersBreaking Change Note
This is an intentional behavior change for
.use()with trailing-slash mount paths. Requests without the trailing slash will no longer match. This is consistent with the5.xlabel on expressjs/express#2281. Users who relied on the old behavior can remove the trailing slash from their mount path (e.g..use('/api', handler)instead of.use('/api/', handler)).Fixes: expressjs/express#2281
AI View (DCCE Protocol v1.0)
Metadata
AI Contribution Summary
Verification Steps Performed
loosen()+trimPrefixinteractionpillarjs/routerAND Express.js 5.2.1Human Review Guidance
trimPrefixcheck inindex.js— this is the core fix/:version/) should also be affectedtest/router.jschanges show both the updated existing test and all new coverageMade with M7 Cursor