MCP Security Implementation Guide

    Blocking symlink and path traversal escapes in the Filesystem MCP server

    To prevent path traversal and symlink escapes in the Filesystem MCP server, upgrade to a fixed release (0.6.4 or 2025.7.01 or later for CVE-2025-53109), resolve every path to its real location before comparing it with the allowed directories, reject traversal and encoded patterns, handle symlink and race-condition edge cases, and run the server with least-privilege permissions in an isolated environment. Runtime policy enforcement on tool calls adds a block decision and evidence in the execution path.

    Regression tests that prove the fix

    • A symlink inside an allowed directory pointing to a file outside it: read and write must fail.
    • A sibling directory sharing the allowed directory's name as a prefix must be denied.
    • Plain, encoded, double-encoded and Unicode-variant ../ sequences must be rejected.
    • Subdirectories of any restricted path must be denied, not only the exact path.
    • A symlink swapped in after validation, repeated under concurrency, must not expose the target.
    • Every other tool that takes a path argument must reject out-of-scope targets.

    How these escapes work

    The Filesystem MCP server lets an agent read and write files through tool calls, and it limits access to a configured set of allowed directories. That boundary is only as strong as the check that enforces it. A check that compares the requested path with an allowed directory as plain text can be defeated by a path that looks inside the boundary but resolves outside it.

    Two patterns matter most. The first is the symlink. A symbolic link inside an allowed directory can point at a location elsewhere on the host. The path string begins with the allowed prefix, so a string comparison passes, yet the operating system follows the link and opens the target. NVD records that versions of the reference Filesystem server before 0.6.4 or 2025.7.01 could allow access to unintended files through symlinks within allowed directories (CVE-2025-53109). The second is the traversal sequence. Segments such as ../ or their encoded variants can walk out of a directory after a prefix check has already passed.

    The wider MCP ecosystem shows the same class of flaw. Zen MCP Server before 9.8.2 (CVE-2025-66689) validated paths by exact string match against a blacklist of system directories, so subdirectories of blacklisted paths slipped through. Another MCP server product before version 0.30.0 failed to validate paths in tool arguments, which allowed files outside the project directory to be read, created, overwritten and deleted. In mcp-server-git, the git_init tool accepted arbitrary paths, and the maintainers removed the tool. These are not Filesystem server bugs, but they show where the attack surface sits: the path argument in the tool call.

    Impact is amplified because MCP servers act with delegated user permissions and chained tool calls. A prompt-injected instruction does not need to defeat the model. It only needs to supply a path the server fails to contain.

    Canonicalize first, then check containment

    The core fix is ordering. Resolve the path to its real, absolute location, and only then compare it with the allowed directories. Allowlist containment is the right model. The Zen MCP Server bypass shows why a denylist or exact-match check fails: the attacker only has to name something the list did not anticipate.

    Resolve the allowed directories themselves at startup, so the comparison is between two real paths. Compare on path-segment boundaries, not raw string prefixes, otherwise a directory named /data/allowed-archive passes a check for /data/allowed. Account for case-insensitive filesystems where relevant.

    Treat encoding as a validation problem, not a cleaning problem. Decode input once, in one place, and reject the request if the result contains traversal segments, null bytes or unexpected encodings such as double-encoded separators. Normalize Unicode before checking, and reject rather than strip, because stripping can itself reassemble a traversal sequence. Follow OWASP's guidance that the caller should never supply every part of the path: join the untrusted portion to a base you control.

    • Reject absolute paths and traversal segments in tool arguments before any filesystem call.
    • Decode once, normalize Unicode, and refuse null bytes and double encoding.
    • Resolve the real path of the target, including every symlink in the chain.
    • Resolve the allowed directories at startup and compare on segment boundaries.
    • Apply the same validation to every tool that accepts a path, not only the main file tools.

    Symlinks, hardlinks and race conditions

    Real-path resolution handles the common symlink case, but it has limits. For writes, the target may not exist yet, so resolve the parent directory and validate that instead. For reads, a path that is safe at check time may not be safe at use time.

    That gap is a time-of-check/time-of-use race. If the server validates a path and then opens it as a separate step, a process with write access to the allowed directory can swap a regular file for a symlink in between. Narrow the window by opening the file first and validating what was actually opened, by refusing to follow symlinks on the final path component where the platform supports it, and by using directory-relative opens that cannot resolve above a given root. Where the kernel offers resolve-beneath semantics, prefer them over user-space checks. No user-space check fully closes the race on its own, which is why the controls in the next section matter.

    Hardlinks are a different problem. A hardlink is an additional name for the same file, so real-path resolution sees an ordinary file inside the boundary. Prevent them structurally: keep allowed directories on a separate volume from sensitive data, because hardlinks cannot span filesystems, and do not give the server account permission to create links to files it does not own. The simplest policy is to forbid link creation through the server entirely and to disallow symlinks in directories that agents can write to.

    Harden the operating environment

    Application-level validation will have gaps, so bound what a bug can reach. OWASP lists sandbox escapes as a key MCP risk: a local server with full host access turns a traversal flaw into file system traversal, credential theft or code execution. Do not run the server as your own user or with broad host access.

    Run it under a dedicated service account with read and write permission only on the intended directories. Where agents only need to read, mount those directories read-only. In containers, NIST SP 800-190 recommends read-only root filesystems, which isolate tampering to specific locations, and mandatory access control, which restricts a containerized application to specific files, paths and processes even if it is compromised. Applying these to a Filesystem MCP deployment is a straightforward extension of that guidance. Keep secrets, SSH keys and cloud credentials out of the server's mount namespace entirely, so a successful escape finds nothing worth reading.

    Operationally, keep an inventory of deployed MCP server versions and map them to advisories such as CVE-2025-53109. Track path-handling flaws as CWE-22 findings in vulnerability management, which is how NVD classifies the related entries.

    Where runtime policy enforcement fits

    Server-side validation and OS isolation prevent most escapes, but neither produces a record that a regulator or auditor can read. Enforcement at the tool-call layer adds that.

    Trussed enforces policy in the execution path on MCP tool calls. File-path arguments can be allowed, redacted or blocked at runtime, rather than reviewed after the fact, and each decision generates audit-ready evidence. This sits alongside the controls above and does not replace them. A call that names a path outside the approved scope is stopped before it reaches the server, and the blocked attempt is recorded as evidence that out-of-scope access was prevented.

    For a regulated enterprise, that distinction matters: a patched server shows a flaw was fixed, while evidence from enforcement shows what the agent was allowed to do and what happened on a specific call.

    Layered containment for file tool calls

    Validate

    Resolve the real path, then compare it with allowed directories.

    Reject

    Refuse traversal and encoded-path patterns instead of cleaning them.

    Isolate

    Run with a least-privilege account and restricted mounts.

    Enforce and record

    Apply policy to tool-call arguments and keep evidence of each decision.

    Add a block decision and evidence to MCP file access

    See how policy enforcement on MCP tool calls blocks out-of-scope file paths and produces audit-ready evidence.

    See How Evidence Is Produced