MCPSafe.io
RegistryThreatsMethodologyDocsPricingScanSign in
MCPSafe.io

Security checks for MCP servers — public packages and private repos, fast or deep.

Legal

Privacy PolicyCookie PolicyTerms of ServiceSecurity disclosure

Resources

State of MCP SecuritySupportSystem statusMade in Germany 🇩🇪

© 2026 MCPSafe. All rights reserved.

GDPR — Privacy Policy
← Threat Catalog

Server Implementation

Resources/read exposes arbitrary filesystem paths

HIGHCWE: CWE-22Rule: MCP-212

An MCP `resources/read` handler accepts a URI and reads it from disk without resolving and containing the path, letting a model read any file the server process can access. Structurally identical to MCP-003 path traversal but scoped to the `resources/read` primitive instead of tool arguments — both can fire on the same server.

What it is

MCP exposes a `resources/read` handler that maps a URI (e.g. `file:///notes/agenda.md`) to a file. If the handler joins the URI's path component to a base directory without resolution + containment, traversal sequences (`../`, absolute paths, symlinks) escape the intended scope. The bug is structurally identical to MCP-003 path traversal but lives at the resources layer rather than a tool argument.

Why it matters for MCP

`resources/read` is invoked transparently by MCP clients during normal context retrieval — the user often never sees the URI being requested. A model that's been prompt-injected into asking for `file:///etc/passwd` can read it back through the MCP channel without the user ever clicking anything.

Vulnerable example

example.py
1
BASE = "/srv/notes"
2
3
@server.resource_handler("read")
4
async def read_resource(uri: str) -> str:
5
    path = os.path.join(BASE, uri.removeprefix("file://"))
6
    with open(path) as f:
7
        return f.read()

Secure example

example.py
1
from pathlib import Path
2
BASE = Path("/srv/notes").resolve()
3
4
@server.resource_handler("read")
5
async def read_resource(uri: str) -> str:
6
    rel = uri.removeprefix("file://").lstrip("/")
7
    candidate = (BASE / rel).resolve()
8
    if BASE not in candidate.parents and candidate != BASE:
9
        raise ValueError("path outside scope")
10
    return candidate.read_text()

How MCPSafe detects this

MCPSafe flags `resources/read` handlers that pass the URI argument into `open`, `Path(...).read_text`, `fs.readFile`, or equivalent without an intervening containment check (`Path.resolve()` + `is_relative_to`, or `path.resolve()` + `startsWith(base)`).

See the full threat catalog for every documented detection.

Further reading

  • MCP Spec — Resources
  • CWE-22: Path Traversal

Scan an MCP server for this issue

MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.

Scan now