René's URL Explorer Experiment


Title: fix: reject URL-encoded path separators in resource template parameters by sebastiondev · Pull Request #2353 · modelcontextprotocol/python-sdk · GitHub

Open Graph Title: fix: reject URL-encoded path separators in resource template parameters by sebastiondev · Pull Request #2353 · modelcontextprotocol/python-sdk

X Title: fix: reject URL-encoded path separators in resource template parameters by sebastiondev · Pull Request #2353 · modelcontextprotocol/python-sdk

Description: Vulnerability Summary CWE-22: Improper Limitation of a Pathname to a Restricted Directory ("Path Traversal") Severity: High Affected file: src/mcp/server/mcpserver/resources/templates.py — ResourceTemplate.matches() Data Flow A client sends an MCP resources/read request with a crafted URI (e.g. file://documents/../../../etc/passwd). MCPServer._handle_read_resource() routes the request to ResourceManager.get_resource() (line 94, resource_manager.py). ResourceTemplate.matches() converts the URI template to a regex where each {param} becomes (?P[^/]+). The regex matches the encoded URI — ../../../etc/passwd contains no literal /, so [^/]+ succeeds. unquote() then decodes it to ../../../etc/passwd, which is returned as the parameter value. If the server-side resource handler uses this value in file-system operations (a common pattern shown in official examples), path traversal occurs. Exploit Sketch # Server code (common pattern from official examples) @mcp.resource("file://documents/{name}") def read_document(name: str) -> str: return Path(f"/data/documents/{name}").read_text() // Attack payload {"method": "resources/read", "params": {"uri": "file://documents/../../../secrets/api_keys.json"}} Before fix: name = "../../../secrets/api_keys.json" → resolves to /secrets/api_keys.json After fix: matches() returns None → request is rejected Fix Description The fix adds a post-decode validation step in ResourceTemplate.matches(). After URL-decoding extracted parameter values, each value is re-validated against the same [^/]+ segment constraint that was applied to the encoded form. If a decoded value contains a / character (which could only come from an encoded /), matches() returns None. Rationale Single chokepoint: matches() is the only code path that extracts template parameters from URIs in the MCP server framework. Fixing it here provides defense-in-depth for all resource handlers. Minimal and targeted: 1 file changed, 21 insertions, 1 deletion. No behavioral change for legitimate URIs (which never contain encoded path separators in parameter segments). Consistent with RFC 3986 §2.2: / is a reserved character; / in a path segment should not be treated as equivalent to a literal / for matching purposes. Prior art: This is the same class of vulnerability as CVE-2021-41773 (Apache httpd path traversal via . encoding) and similar issues fixed in Flask/Werkzeug. Change Details # New module-level constant _SEGMENT_RE = re.compile(r"[^/]+") # In matches(), after decoding: decoded = {key: unquote(value) for key, value in match.groupdict().items()} for value in decoded.values(): if not _SEGMENT_RE.fullmatch(value): return None return decoded Test Results Summary Tested the following scenarios to confirm correctness: Test Case Input URI Expected Result Normal match file://docs/readme.txt {"name": "readme.txt"} ✅ Pass Encoded slash / file://docs/../../etc/passwd None (rejected) ✅ Pass Encoded slash / (lowercase) file://docs/../../etc/passwd None (rejected) ✅ Pass Double-encoded %2F file://docs/..%2F..%2Fetc {"name": "../../etc"} (single decode, no /) ✅ Pass Legitimate encoded chars file://docs/hello world.txt {"name": "hello world.txt"} ✅ Pass Multiple parameters file://{org}/{repo} with / in one param None (rejected) ✅ Pass Empty-after-decode file://docs// None (rejected) ✅ Pass Non-matching URI file://other/path against file://docs/{name} None ✅ Pass (no regression) Disprove Analysis We systematically attempted to disprove this finding across multiple dimensions: Authentication Check No authentication on ResourceTemplate.matches() or read_resource. Auth is opt-in via MCPServer(auth=...). Default servers run without auth. The resources/read handler is accessible to any connected MCP client. Network Check Default transport is stdio (local process). HTTP transports default to 127.0.0.1 with DNS rebinding protection. However, servers can be configured to bind to 0.0.0.0 or deployed over HTTP — the README shows streamable HTTP examples suggesting production HTTP use is expected. Caller Trace matches() is called from exactly one place: ResourceManager.get_resource() → MCPServer.read_resource() → _handle_read_resource(). The URI comes directly from params.uri (client-supplied). Attacker-controlled data reaches this code path. Prior Validation Zero validation existed on decoded parameter values before this fix. Pydantic validate_call only enforces type (str), not content. Existing Mitigations Default stdio transport reduces remote attack surface HTTP transport defaults to localhost with DNS rebinding protection OAuth auth is available but opt-in No framework-level path sanitization beyond this fix Similar Code Paths The low-level server API does not use ResourceTemplate — it delegates to user callbacks directly. This is not a parallel vulnerability because low-level users don't use templates and are responsible for their own parsing. No other instances of template parameter extraction without post-decode validation exist. Known Limitation The fix does not block \ → \ (Windows backslash path separator). On Windows, ..\..\secret is a valid traversal. This is a separate concern given the URI spec uses / exclusively, and could be addressed in a follow-up if desired. Verdict Confirmed valid with high confidence. The vulnerability is clearly demonstrable, the fix is correct and minimal, and it addresses the primary attack vector at the single chokepoint for template parameter extraction. Preconditions for Exploitation MCP client access: Attacker must send MCP protocol messages (via compromised/prompt-injected LLM, direct HTTP connection, or local stdio) Vulnerable resource handler: Server must use extracted template parameters in file-system operations without additional validation No additional path sanitization in user handler code This fix was prepared through a 4-stage review process including vulnerability identification, fix development, testing, and adversarial disprove analysis.

Open Graph Description: Vulnerability Summary CWE-22: Improper Limitation of a Pathname to a Restricted Directory ("Path Traversal") Severity: High Affected file: src/mcp/server/mcpserver/resources/templates.py ...

X Description: Vulnerability Summary CWE-22: Improper Limitation of a Pathname to a Restricted Directory ("Path Traversal") Severity: High Affected file: src/mcp/server/mcpserver/resources/templ...

Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/pull/2353

X: @github

direct link

Domain: github.com

route-pattern/:user_id/:repository/pull/:id/files(.:format)
route-controllerpull_requests
route-actionfiles
fetch-noncev2:91edcbb7-ef36-cde0-37f9-6bf4074d50d9
current-catalog-service-hashae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b
request-idA14C:23C361:19FF192:24E024C:6A58FFEF
html-safe-nonce82392575b4bf3f1fa4e15a4fdf55388cca2e6e320ecaba6c645837c3b9cc9f3f
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBMTRDOjIzQzM2MToxOUZGMTkyOjI0RTAyNEM6NkE1OEZGRUYiLCJ2aXNpdG9yX2lkIjoiMjQ2OTc4NjU2NDEzNTgxMzEwMyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9
visitor-hmacfb21eec067ee1d7cd602025a536bbf44141394a2d265795c3a0253cea0c1779d
hovercard-subject-tagpull_request:3450869360
github-keyboard-shortcutsrepository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///pull_requests/show/files
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/modelcontextprotocol/python-sdk/pull/2353/files
twitter:imagehttps://avatars.githubusercontent.com/u/262984339?s=400&v=4
twitter:cardsummary_large_image
og:imagehttps://avatars.githubusercontent.com/u/262984339?s=400&v=4
og:image:altVulnerability Summary CWE-22: Improper Limitation of a Pathname to a Restricted Directory ("Path Traversal") Severity: High Affected file: src/mcp/server/mcpserver/resources/templates.py ...
og:site_nameGitHub
og:typeobject
hostnamegithub.com
expected-hostnamegithub.com
None5f2a0c7865178af3d91dd9f13b0cdfc3c73a2529c873d2780bb4c01271a57ec6
turbo-cache-controlno-preview
diff-viewunified
go-importgithub.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git
octolytics-dimension-user_id182288589
octolytics-dimension-user_loginmodelcontextprotocol
octolytics-dimension-repository_id862584018
octolytics-dimension-repository_nwomodelcontextprotocol/python-sdk
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id862584018
octolytics-dimension-repository_network_root_nwomodelcontextprotocol/python-sdk
turbo-body-classeslogged-out env-production page-responsive
disable-turbotrue
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release9842457e8d7e3b2e1635a63af28da35614845510
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/modelcontextprotocol/python-sdk/pull/2353/files#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fmodelcontextprotocol%2Fpython-sdk%2Fpull%2F2353%2Ffiles
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub Copilot appDirect agents from issue to mergehttps://github.com/features/ai/github-app
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
View all resourceshttps://github.com/resources
GitHub SponsorsFund open source developershttps://github.com/open-source/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/open-source/accelerator
GitHub Starshttps://stars.github.com
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fmodelcontextprotocol%2Fpython-sdk%2Fpull%2F2353%2Ffiles
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fpull_requests%2Fshow%2Ffiles&source=header-repo&source_repo=modelcontextprotocol%2Fpython-sdk
Reloadhttps://github.com/modelcontextprotocol/python-sdk/pull/2353/files
Reloadhttps://github.com/modelcontextprotocol/python-sdk/pull/2353/files
Reloadhttps://github.com/modelcontextprotocol/python-sdk/pull/2353/files
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/2353/files
modelcontextprotocol https://github.com/modelcontextprotocol
python-sdkhttps://github.com/modelcontextprotocol/python-sdk
Notifications https://github.com/login?return_to=%2Fmodelcontextprotocol%2Fpython-sdk
Fork 3.7k https://github.com/login?return_to=%2Fmodelcontextprotocol%2Fpython-sdk
Star 23.6k https://github.com/login?return_to=%2Fmodelcontextprotocol%2Fpython-sdk
Code https://github.com/modelcontextprotocol/python-sdk
Issues 255 https://github.com/modelcontextprotocol/python-sdk/issues
Pull requests 291 https://github.com/modelcontextprotocol/python-sdk/pulls
Actions https://github.com/modelcontextprotocol/python-sdk/actions
Projects https://github.com/modelcontextprotocol/python-sdk/projects
Models https://github.com/modelcontextprotocol/python-sdk/models
Security and quality 6 https://github.com/modelcontextprotocol/python-sdk/security
Insights https://github.com/modelcontextprotocol/python-sdk/pulse
Code https://github.com/modelcontextprotocol/python-sdk
Issues https://github.com/modelcontextprotocol/python-sdk/issues
Pull requests https://github.com/modelcontextprotocol/python-sdk/pulls
Actions https://github.com/modelcontextprotocol/python-sdk/actions
Projects https://github.com/modelcontextprotocol/python-sdk/projects
Models https://github.com/modelcontextprotocol/python-sdk/models
Security and quality https://github.com/modelcontextprotocol/python-sdk/security
Insights https://github.com/modelcontextprotocol/python-sdk/pulse
Sign up for GitHub https://github.com/signup?return_to=%2Fmodelcontextprotocol%2Fpython-sdk%2Fissues%2Fnew%2Fchoose
terms of servicehttps://docs.github.com/terms
privacy statementhttps://docs.github.com/privacy
Sign inhttps://github.com/login?return_to=%2Fmodelcontextprotocol%2Fpython-sdk%2Fissues%2Fnew%2Fchoose
sebastiondevhttps://github.com/sebastiondev
modelcontextprotocol:mainhttps://github.com/modelcontextprotocol/python-sdk/tree/main
sebastiondev:security/fix-path-traversal-in-resource-templateshttps://github.com/sebastiondev/python-sdk/tree/security/fix-path-traversal-in-resource-templates
Conversation 1 https://github.com/modelcontextprotocol/python-sdk/pull/2353
Commits 1 https://github.com/modelcontextprotocol/python-sdk/pull/2353/commits
Checks 26 https://github.com/modelcontextprotocol/python-sdk/pull/2353/checks
Files changed https://github.com/modelcontextprotocol/python-sdk/pull/2353/files
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/2353/files
fix: reject URL-encoded path separators in resource template parameters https://github.com/modelcontextprotocol/python-sdk/pull/2353/files#top
Show all changes 1 commit https://github.com/modelcontextprotocol/python-sdk/pull/2353/files
29aacce fix: reject URL-encoded path separators in resource template parameters sebastiondev Mar 26, 2026 https://github.com/modelcontextprotocol/python-sdk/pull/2353/commits/29aacce949a5575302371bdd86847daf6f20fde7
Clear filters https://github.com/modelcontextprotocol/python-sdk/pull/2353/files
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/2353/files
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/2353/files
src/mcp/server/mcpserver/resources/templates.pyhttps://github.com/modelcontextprotocol/python-sdk/pull/2353/files#diff-44c89eefb9a0f376f6c3004ad4a44828e0403a96114fe008c0dd7a708abde7cc
View file https://github.com/sebastiondev/python-sdk/blob/29aacce949a5575302371bdd86847daf6f20fde7/src/mcp/server/mcpserver/resources/templates.py
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/modelcontextprotocol/python-sdk/pull/2353/{{ revealButtonHref }}
https://github.com/modelcontextprotocol/python-sdk/pull/2353/files#diff-44c89eefb9a0f376f6c3004ad4a44828e0403a96114fe008c0dd7a708abde7cc
https://github.com/modelcontextprotocol/python-sdk/pull/2353/files#diff-44c89eefb9a0f376f6c3004ad4a44828e0403a96114fe008c0dd7a708abde7cc
https://github.com/modelcontextprotocol/python-sdk/pull/2353/files#diff-44c89eefb9a0f376f6c3004ad4a44828e0403a96114fe008c0dd7a708abde7cc
https://github.com/modelcontextprotocol/python-sdk/pull/2353/files#diff-44c89eefb9a0f376f6c3004ad4a44828e0403a96114fe008c0dd7a708abde7cc
Please reload this pagehttps://github.com/modelcontextprotocol/python-sdk/pull/2353/files
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.