fix(resources): escape literal regex metacharacters in ResourceTemplate.matches#2749
fix(resources): escape literal regex metacharacters in ResourceTemplate.matches#2749vidigoat wants to merge 1 commit into
Conversation
ResourceTemplate.matches() built its regex with a naive string
substitution:
pattern = self.uri_template.replace('{', '(?P<').replace('}', '>[^/]+)')
The literal portions of the template were never re.escape-d, so regex
metacharacters in the template text were interpreted as operators. A
template like 'api://v1.0/{version}' treated '.' as 'any character' and
wrongly matched 'api://v1X0/abc' (false positive routing a URI to the
wrong template), while a template with '+', '(', '[' etc. in a literal
segment failed to match its own valid URIs (false negative).
Tokenize the template into literal/placeholder parts, re.escape the
literals, and turn '{param}' into named capture groups. Adds a
regression test.
Signed-off-by: Vidit Patankar <vidit.patankar16@gmail.com>
StantonMatt
left a comment
There was a problem hiding this comment.
I checked this against the resource-manager routing surface, since the bad . behavior can let an earlier template catch a URI intended for a later one. With api://v1.0/{version} registered before api://v1X0/{version}, ResourceManager.get_resource(api://v1X0/abc) now returns the v1X0 template result (x:abc), while api://v1.0/abc still returns the dotted template result (dot:abc).
Local checks:
uv run --frozen pytest tests/server/mcpserver/resources/test_resource_template.py tests/issues/test_973_url_decoding.py -quv run --frozen ruff check src/mcp/server/mcpserver/resources/templates.py tests/server/mcpserver/resources/test_resource_template.pygit diff --check origin/main...HEAD
|
Gentle nudge on this one whenever a maintainer has a moment. CI is green, and @StantonMatt independently reproduced the template-routing issue and verified the fix against the resource-manager surface. It's a small, self-contained change (escaping literal regex metacharacters in |
Problem
ResourceTemplate.matches()(src/mcp/server/mcpserver/resources/templates.py) builds its matching regex by naive string substitution:The literal portions of the template are never
re.escape-d, so any regex metacharacter in the template text is interpreted as a regex operator. This causes both:api://v1.0/{version}treats.as "any character" and wrongly matchesapi://v1X0/abc, routing a URI to a template it shouldn't match (returning{'version': 'abc'}instead ofNone).+,(,[, etc. in a literal segment (e.g.res://a+b/{x}) fails to match its own valid URI.Fix
Tokenize the template into literal/placeholder parts,
re.escapethe literal text, and turn each{param}into a named capture group:Verification
ruff checkandruff format --checkpass on both changed files. Adds a regression test asserting metacharacters in literal segments are matched literally.