mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-28 02:10:38 +02:00
Clarify resource path parameter safety (#4398)
This commit is contained in:
parent
a8bb1b08c2
commit
6b2a2d507a
2 changed files with 60 additions and 4 deletions
|
|
@ -477,7 +477,7 @@ FastMCP implements [RFC 6570 URI Templates](https://datatracker.ietf.org/doc/htm
|
|||
|
||||
<VersionBadge version="2.2.4" />
|
||||
|
||||
Resource templates support wildcard parameters that can match multiple path segments. While standard parameters (`{param}`) only match a single path segment and don't cross "/" boundaries, wildcard parameters (`{param*}`) can capture multiple segments including slashes. Wildcards capture all subsequent path segments *up until* the defined part of the URI template (whether literal or another parameter). This allows you to have multiple wildcard parameters in a single URI template.
|
||||
Resource templates support wildcard parameters that can match multiple path segments. Standard parameters (`{param}`) match a single URI segment before decoding and do not cross literal "/" boundaries in the request URI. Wildcard parameters (`{param*}`) can capture multiple segments including slashes. Wildcards capture all subsequent path segments *up until* the defined part of the URI template (whether literal or another parameter). This allows you to have multiple wildcard parameters in a single URI template.
|
||||
|
||||
```python {15, 23}
|
||||
from fastmcp import FastMCP
|
||||
|
|
@ -522,6 +522,34 @@ Wildcard parameters are useful when:
|
|||
|
||||
Note that like regular parameters, each wildcard parameter must still be a named parameter in your function signature, and all required function parameters must appear in the URI template.
|
||||
|
||||
#### Filesystem Path Safety
|
||||
|
||||
Template parameters are decoded before your function receives them. A standard `{filename}` parameter matches one URI segment before decoding, so a request like `files://a%2Fb` passes `filename="a/b"` to the handler. Treat template values as untrusted decoded URI data whenever they determine filesystem paths.
|
||||
|
||||
Validate the final resolved path against an allowed root before reading:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.exceptions import ResourceError
|
||||
|
||||
mcp = FastMCP(name="DocsServer")
|
||||
DOCS_ROOT = Path("docs").resolve()
|
||||
|
||||
|
||||
@mcp.resource("docs://{filename}")
|
||||
def read_doc(filename: str) -> str:
|
||||
requested_path = (DOCS_ROOT / filename).resolve()
|
||||
|
||||
if not requested_path.is_relative_to(DOCS_ROOT) or not requested_path.is_file():
|
||||
raise ResourceError("Document not found")
|
||||
|
||||
return requested_path.read_text(encoding="utf-8")
|
||||
```
|
||||
|
||||
Use wildcard parameters (`{path*}`) for resources whose URI shape intentionally includes slashes, and apply the same containment check before accessing the filesystem.
|
||||
|
||||
#### Query Parameters
|
||||
|
||||
<VersionBadge version="2.13.0" />
|
||||
|
|
@ -716,4 +744,4 @@ The duplicate behavior options are:
|
|||
|
||||
<VersionBadge version="3.0.0" />
|
||||
|
||||
Resources and resource templates support versioning, allowing you to maintain multiple implementations under the same URI while clients automatically receive the highest version. See [Versioning](/servers/versioning) for complete documentation on version comparison, retrieval, and migration patterns.
|
||||
Resources and resource templates support versioning, allowing you to maintain multiple implementations under the same URI while clients automatically receive the highest version. See [Versioning](/servers/versioning) for complete documentation on version comparison, retrieval, and migration patterns.
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ FastMCP implements [RFC 6570 URI Templates](https://datatracker.ietf.org/doc/htm
|
|||
|
||||
<VersionBadge version="2.2.4" />
|
||||
|
||||
Resource templates support wildcard parameters that can match multiple path segments. While standard parameters (`{param}`) only match a single path segment and don't cross "/" boundaries, wildcard parameters (`{param*}`) can capture multiple segments including slashes. Wildcards capture all subsequent path segments *up until* the defined part of the URI template (whether literal or another parameter). This allows you to have multiple wildcard parameters in a single URI template.
|
||||
Resource templates support wildcard parameters that can match multiple path segments. Standard parameters (`{param}`) match a single URI segment before decoding and do not cross literal "/" boundaries in the request URI. Wildcard parameters (`{param*}`) can capture multiple segments including slashes. Wildcards capture all subsequent path segments *up until* the defined part of the URI template (whether literal or another parameter). This allows you to have multiple wildcard parameters in a single URI template.
|
||||
|
||||
```python {15, 23}
|
||||
from fastmcp import FastMCP
|
||||
|
|
@ -446,6 +446,34 @@ Wildcard parameters are useful when:
|
|||
|
||||
Note that like regular parameters, each wildcard parameter must still be a named parameter in your function signature, and all required function parameters must appear in the URI template.
|
||||
|
||||
#### Filesystem Path Safety
|
||||
|
||||
Template parameters are decoded before your function receives them. A standard `{filename}` parameter matches one URI segment before decoding, so a request like `files://a%2Fb` passes `filename="a/b"` to the handler. Treat template values as untrusted decoded URI data whenever they determine filesystem paths.
|
||||
|
||||
Validate the final resolved path against an allowed root before reading:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.exceptions import ResourceError
|
||||
|
||||
mcp = FastMCP(name="DocsServer")
|
||||
DOCS_ROOT = Path("docs").resolve()
|
||||
|
||||
|
||||
@mcp.resource("docs://{filename}")
|
||||
def read_doc(filename: str) -> str:
|
||||
requested_path = (DOCS_ROOT / filename).resolve()
|
||||
|
||||
if not requested_path.is_relative_to(DOCS_ROOT) or not requested_path.is_file():
|
||||
raise ResourceError("Document not found")
|
||||
|
||||
return requested_path.read_text(encoding="utf-8")
|
||||
```
|
||||
|
||||
Use wildcard parameters (`{path*}`) for resources whose URI shape intentionally includes slashes, and apply the same containment check before accessing the filesystem.
|
||||
|
||||
#### Query Parameters
|
||||
|
||||
<VersionBadge version="2.13.0" />
|
||||
|
|
@ -634,4 +662,4 @@ The duplicate behavior options are:
|
|||
- `"warn"` (default): Logs a warning, and the new resource/template replaces the old one.
|
||||
- `"error"`: Raises a `ValueError`, preventing the duplicate registration.
|
||||
- `"replace"`: Silently replaces the existing resource/template with the new one.
|
||||
- `"ignore"`: Keeps the original resource/template and ignores the new registration attempt.
|
||||
- `"ignore"`: Keeps the original resource/template and ignores the new registration attempt.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue