You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
check_resource_allowed treats /api/../admin as under /api #3464
check_resource_allowed pads trailing slashes and then does requested_path.startswith(configured_path). It does not collapse . / ...
On main @ 08a3bc8 these return True:
requested https://example.com/api/../admin vs configured https://example.com/api
requested https://example.com/mcp/.. vs configured https://example.com/mcp
requested https://example.com/api/%2e%2e/admin vs configured https://example.com/api
tests/shared/test_auth_utils.py already rejects /api123 vs /api (path-boundary). Dot-segments are not covered.
What I expected
A requested path that walks out of the configured prefix should not match. /api/foo/../v1 vs /api can still match after normalisation, because it stays under /api.
How to reproduce
frommcp.shared.auth_utilsimportcheck_resource_allowedcheck_resource_allowed("https://example.com/api/../admin", "https://example.com/api")
# True today. I expected False.
I can send a PR that percent-decodes once, runs posixpath.normpath, then keeps the existing trailing-slash prefix rule. Happy to do that if you want it.
Written with AI assistance. I read auth_utils.py next to the path-boundary tests and reproduced it locally.
I'd like to work on this. I'll first verify the current check_resource_allowed behavior and existing path-boundary tests, then add regression coverage for dot-segments and percent-encoded traversal while preserving the existing prefix semantics. I'll also check how URL paths are parsed/decoded elsewhere in the SDK before settling on the normalization approach.
Root cause: check_resource_allowed does a naive prefix check on the URI path. A request to /api/../admin passes the /api prefix check because the path is not normalized before comparison.
Approach: Normalize the request path with posixpath.normpath (or equivalent) before the prefix check, resolving .. and . segments. This ensures /api/../admin correctly resolves to /admin and gets rejected.
I will include test cases for various traversal patterns (.., encoded variants).
check_resource_allowed("https://example.com/api/../admin", "https://example.com/api") evaluates to True because trailing-slash padding produces /api/../admin/ which naively .startswith("/api/").
The same applies to /mcp/.. and percent-encoded variants (%2e%2e / %2E%2E).
To address this cleanly without breaking existing prefix semantics:
Decode percent-encoded characters via urllib.parse.unquote() and normalize with posixpath.normpath() (ensuring a leading /).
Re-apply trailing slash normalization (/foo and /foo/ equivalence) so path-boundary protection against prefix collisions (like /api123/ vs /api/) is preserved.
Apply this normalization to both requested_path and configured_path before checking requested_path.startswith(configured_path).
I have this implemented and verified locally with comprehensive regression tests covering out-of-boundary traversal (.., %2e%2e, ../../admin, /mcp/..), intra-boundary traversals that legitimately stay under the configured root (/api/v1/../v2, /api/./v1), and all 16 existing auth tests passing cleanly.
Happy to open the PR if maintainers would like to assign this or open it for external contributions!
Written with AI assistance; reproduced and tested locally.
Both reports reproduce the same check_resource_allowed path-prefix bypass using raw and percent-encoded dot segments. This is tracked in #3303, so I’m closing this as a duplicate. AI-assisted triage; I reviewed both reports.
Activity
AbarnaaSree commentedon Sep 7, 2026
I'd like to work on this. I'll first verify the current
check_resource_allowedbehavior and existing path-boundary tests, then add regression coverage for dot-segments and percent-encoded traversal while preserving the existing prefix semantics. I'll also check how URL paths are parsed/decoded elsewhere in the SDK before settling on the normalization approach.zsxh1990 commentedon Sep 7, 2026
I'd like to work on this security fix.
Root cause: check_resource_allowed does a naive prefix check on the URI path. A request to /api/../admin passes the /api prefix check because the path is not normalized before comparison.
Approach: Normalize the request path with posixpath.normpath (or equivalent) before the prefix check, resolving .. and . segments. This ensures /api/../admin correctly resolves to /admin and gets rejected.
I will include test cases for various traversal patterns (.., encoded variants).
1747687484-collab commentedon Sep 8, 2026
Confirmed the reproduction locally on
main:check_resource_allowed("https://example.com/api/../admin", "https://example.com/api")evaluates toTruebecause trailing-slash padding produces/api/../admin/which naively.startswith("/api/")./mcp/..and percent-encoded variants (%2e%2e/%2E%2E).To address this cleanly without breaking existing prefix semantics:
urllib.parse.unquote()and normalize withposixpath.normpath()(ensuring a leading/)./fooand/foo/equivalence) so path-boundary protection against prefix collisions (like/api123/vs/api/) is preserved.requested_pathandconfigured_pathbefore checkingrequested_path.startswith(configured_path).I have this implemented and verified locally with comprehensive regression tests covering out-of-boundary traversal (
..,%2e%2e,../../admin,/mcp/..), intra-boundary traversals that legitimately stay under the configured root (/api/v1/../v2,/api/./v1), and all 16 existing auth tests passing cleanly.Happy to open the PR if maintainers would like to assign this or open it for external contributions!
Written with AI assistance; reproduced and tested locally.
Kludex commentedon Oct 10, 2026
Both reports reproduce the same
check_resource_allowedpath-prefix bypass using raw and percent-encoded dot segments. This is tracked in #3303, so I’m closing this as a duplicate. AI-assisted triage; I reviewed both reports.