T09 · Insecure Skill Coding Practices
Error
- Location
- references/python_mcp_server.md:531
- Finding
- Path Traversal Enables Arbitrary Local File Disclosure<![CDATA[ ## Vulnerability Details **File Location**: `references/python_mcp_server.md`, lines 531-541 **Vulnerability Type**: Unsanitized path construction and arbitrary file read **Risk Level**: High ### Vulnerable Code ```python @mcp.resource("file://documents/{name}") async def get_document(name: str) -> str: '''Expose documents as MCP resources. Resources are useful for static or semi-static data that doesn't require complex parameters. They use URI templates for flexible access. ''' document_path = f"./docs/{name}" with open(document_path, "r") as f: return f.read() ``` ### Technical Analysis The resource handler incorporates the attacker-controlled `name` parameter directly into a filesystem path. It performs no validation for absolute paths, `..` traversal components, symbolic links, or whether the resolved path remains inside the intended `./docs` directory. MCP clients capable of requesting the resource can supply values such as `../../.env` or `../../home/user/.ssh/config`. Python normalizes the traversal components when `open()` resolves the path, allowing the handler to access files outside the intended document directory. This example also conflicts with the project's own best-practice requirement to sanitize paths and prevent directory traversal. Because the code is presented as a reusable implementation pattern, generated MCP servers may inherit the flaw. ### Attack Path 1. A developer implements the documented resource handler without additional validation. 2. An attacker gains access to the MCP resource endpoint. 3. The attacker requests a crafted URI whose `name` contains traversal components, such as `../../.env`. 4. The handler constructs `./docs/../../.env`. 5. `open()` resolves the path outside `./docs` and reads the file under the server process's privileges. 6. The file contents are returned through the MCP resource response. ### Impact Assessment An attacker can read any file accessible to the ...[truncated 519 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Resolve the requested file against a fixed document root and verify containment before opening it: ```python from pathlib import Path DOCUMENT_ROOT = Path("./docs").resolve() @mcp.resource("file://documents/{name}") async def get_document(name: str) -> str: requested = (DOCUMENT_ROOT / name).resolve() if requested == DOCUMENT_ROOT or DOCUMENT_ROOT not in requested.parents: raise ValueError("Invalid document path") if not requested.is_file(): raise FileNotFoundError("Document not found") return requested.read_text(encoding="utf-8") ``` Additional hardening should include: 1. Reject absolute paths and path components equal to `..`. 2. Use an allowlist of permitted filenames or document identifiers where possible. 3. Consider rejecting symbolic links or verify the final resolved target after link resolution. 4. Run the MCP server under an account that cannot read unrelated secrets. 5. Add tests for encoded traversal sequences, absolute paths, symbolic-link escapes, and platform-specific separators. 6. Return generic errors rather than exposing resolved filesystem paths. ]]>
