How Desktop Commander MCP Prevents Symlink Traversal Attacks in File System Operations
Desktop Commander MCP prevents symlink traversal attacks through a multi-layered validation routine in src/tools/filesystem.ts that resolves real paths, validates parent directories for new files, and enforces strict allowlist checks before any file operation.
Desktop Commander MCP is a Model Context Protocol (MCP) server that exposes desktop file system operations to AI agents. Because exposing file system access creates significant security risks, the codebase implements rigorous protections against symlink traversal attacks—attacks where malicious symbolic links redirect operations outside permitted directories.
Understanding Symlink Traversal Attacks
Symlink traversal attacks exploit symbolic links to bypass directory restrictions. An attacker might place a symlink inside an allowed directory that points to sensitive system files (like /etc/passwd), tricking the application into reading or writing to unauthorized locations. Desktop Commander MCP mitigates this threat through a validation pipeline that resolves every path to its canonical form before processing.
Core Security Implementation in validatePath
The validatePath function in src/tools/filesystem.ts serves as the gatekeeper for all file operations. It implements a four-stage verification process that ensures paths cannot escape the configured sandbox.
Real-Path Resolution with fs.realpath
When a path is provided to any file operation, validatePath first calls fs.realpath on the full absolute path. If the path exists, this resolves every component of the chain—including intermediate symlinks—to its canonical target. According to the source code in src/tools/filesystem.ts lines 39-47, this prevents attackers from using relative path components or symlink chains to obfuscate the final destination.
Parent Directory Validation for Non-Existent Targets
For operations targeting new files that do not yet exist, the function cannot resolve the full path. Instead, it walks up the directory tree until it finds the deepest existing ancestor, resolves that parent directory via fs.realpath, and then reconstructs the intended path. As implemented in lines 58-86 of src/tools/filesystem.ts, this prevents an attacker from placing a symlink inside an allowed directory that points to a restricted location, then writing through a non-existent sub-path.
Strict Allowlist Enforcement
After obtaining the resolved real path, the isPathAllowed helper compares it against the configured allowedDirectories. The comparison uses normalized, lower-cased paths with trailing-separator guards to prevent partial-name matches (lines 77-100). Only paths that are literal subdirectories of allowed entries proceed. If validation fails, the function throws an error and logs a server_path_validation_error event (lines 92-99), blocking the operation before any file access occurs.
Automated Security Testing
The repository includes a dedicated test suite at test/test-symlink-security.js that validates these protections. Lines 13-30 create realistic attack scenarios—including directory and file symlinks pointing outside the allowed tree—and assert that validatePath correctly blocks them. Running these tests confirms that the real-path resolution and allowlist checks effectively neutralize symlink-based escape attempts.
Configuration and Practical Usage
To utilize these protections, configure the allowed directories using the configManager before performing operations:
import { configManager } from './config-manager.js';
import { validatePath } from './tools/filesystem.js';
// Restrict operations to a specific safe directory
await configManager.setValue('allowedDirectories', ['/home/claude/projects']);
// Valid access to file within allowed directory
const safePath = await validatePath('projects/readme.md');
// Returns: '/home/claude/projects/readme.md'
// Blocked access through malicious symlink
try {
await validatePath('projects/link_to_passwd');
} catch (e) {
console.error(e.message); // "Path not allowed: projects/link_to_passwd ..."
}
You can verify the symlink protection by executing the dedicated test suite:
npm run test:test-symlink-security
# or
node test/test-symlink-security.js
All tests should pass, confirming that symlink traversal is blocked as documented in the project's SECURITY.md (lines 23-27).
Summary
- Desktop Commander MCP uses a hardened
validatePathfunction insrc/tools/filesystem.tsto prevent symlink traversal attacks. - Real-path resolution via
fs.realpath(lines 39-47) canonicalizes all paths before validation. - Parent directory fallback (lines 58-86) secures operations on non-existent files by validating the deepest existing ancestor.
- Strict allowlist checks (lines 77-100) ensure resolved paths reside within configured
allowedDirectories. - Automated testing in
test/test-symlink-security.js(lines 13-30) continuously verifies attack prevention.
Frequently Asked Questions
How does Desktop Commander MCP handle symlinks in allowed directories?
When validatePath encounters a path, it calls fs.realpath to resolve all symlinks to their actual targets before checking permissions. If a symlink inside an allowed directory points outside the sandbox (e.g., to /etc/passwd), the resolved path fails the isPathAllowed check and the operation is rejected with a server_path_validation_error.
Can an attacker bypass the allowlist by creating new directories through symlinks?
No. For non-existent targets, the code resolves the parent directory first (lines 58-86 in src/tools/filesystem.ts). If the deepest existing ancestor is a symlink pointing outside the allowed tree, fs.realpath exposes the true location, causing the validation to fail before the new file or directory is created.
What happens if the real-path resolution fails or the path escapes the allowlist?
The validatePath function throws an error indicating the path is not allowed (lines 92-99). This error propagates back to the MCP tool caller, preventing any file system operation from executing on the unauthorized path.
Where is the security policy for symlink attacks documented?
The security design and symlink traversal risks are documented in SECURITY.md (lines 23-27) and the README.md security section. The implementation details reside in src/tools/filesystem.ts, while automated verification lives in test/test-symlink-security.js.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →