How Desktop Commander MCP Prevents Symlink Traversal Attacks in File Operations
Desktop Commander MCP blocks symlink traversal attacks by resolving all paths to their canonical locations using fs.realpath, validating parent directories for non-existent targets, and enforcing strict allow-list checks before any file operation executes.
Desktop Commander MCP implements a robust defense against symlink-based directory traversal attacks through its core filesystem validation layer. The validatePath function in src/tools/filesystem.ts applies multiple security checks to ensure AI agents can only access explicitly permitted directories, even when malicious symlinks are present within the allowed tree.
Real-Path Resolution Blocks Symlink Redirection
The first line of defense occurs in src/tools/filesystem.ts at lines 39-47, where the validatePath function calls fs.realpath on the supplied absolute path. This system call resolves every component of the path chain—including intermediate symbolic links—to their canonical filesystem targets before any read or write operation proceeds.
Handling Non-Existent Targets via Parent Directory Validation
When creating new files or directories that do not yet exist, the validation logic walks up the directory tree to find the deepest existing ancestor. As implemented in lines 58-86 of src/tools/filesystem.ts, the code resolves this parent directory using fs.realpath and then reconstructs the intended path. This prevents attackers from exploiting a symlink placed in an allowed directory that points to a restricted location, then writing through a non-existent sub-path.
Strict Allow-List Enforcement with isPathAllowed
After resolving the canonical path, the isPathAllowed function (lines 77-100) performs a normalized comparison against the configured allowedDirectories. The check uses lower-cased paths with trailing-separator guards to prevent partial-name matches, ensuring that /home/user/projects does not inadvertently permit access to /home/user/projects-backup.
Explicit Rejection and Security Event Logging
If the resolved path falls outside all allowed directories, the system throws an explicit error and logs a server_path_validation_error event, as shown in lines 92-99. This fail-closed approach ensures that any attempt to traverse outside the sandbox—whether through symlinks, path normalization tricks, or parent directory references—results in immediate termination of the operation.
Automated Security Testing
The repository includes a dedicated test suite in test/test-symlink-security.js (lines 13-30) that simulates realistic attack scenarios. These tests create both directory and file symlinks pointing outside the allowed tree and assert that validatePath correctly blocks access, providing continuous verification of the security controls as documented in SECURITY.md (lines 23-27).
Configuration and Implementation Examples
To configure the security boundaries:
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 within allowed directory
const safePath = await validatePath('projects/readme.md');
// Returns: '/home/claude/projects/readme.md'
Attack scenario demonstration:
// Attempting to access a malicious symlink pointing to /etc/passwd
try {
await validatePath('projects/link_to_passwd');
} catch (e) {
console.error(e.message);
// Output: "Path not allowed: projects/link_to_passwd ..."
}
Run the security tests:
npm run test:test-symlink-security
# or
node test/test-symlink-security.js
Summary
- Canonical path resolution: All paths are resolved using
fs.realpathinsrc/tools/filesystem.tsbefore validation, eliminating symlink redirection. - Parent directory fallback: For non-existent targets, the system validates the deepest existing ancestor to prevent symlink-based write attacks.
- Strict allow-list checking: The
isPathAllowedfunction enforces configured boundaries with normalized, case-insensitive comparisons. - Comprehensive test coverage: The
test/test-symlink-security.jssuite continuously validates protection against symlink traversal vectors. - Security policy documentation: Implementation details are documented in
SECURITY.mdas a known risk mitigation.
Frequently Asked Questions
How does Desktop Commander MCP handle symlinks that point to allowed directories from outside locations?
When a symlink exists outside the allowed directories but points inside, the validatePath function resolves the symlink target using fs.realpath. Since the resolution happens before the allow-list check, the resulting canonical path must still fall within the configured allowedDirectories to proceed. If the symlink originates from a blocked location, the resolved path validation will fail.
Can an attacker bypass the security by creating a symlink to a parent directory within an allowed folder?
No. The parent directory validation logic in src/tools/filesystem.ts (lines 58-86) resolves the deepest existing ancestor using fs.realpath before reconstructing the target path. If an attacker places a symlink named "parent" pointing to / inside an allowed directory, attempting to write to "parent/etc/passwd" would resolve the symlink to / and immediately fail the allow-list check.
What happens if the allowed directory configuration contains symlinks?
The system normalizes all paths before comparison using fs.realpath. If the allowed directory itself contains symlinks, they are resolved to their canonical targets during the validation process, ensuring that the security boundaries align with actual filesystem locations rather than symlink aliases.
Is the symlink protection tested automatically in CI/CD?
Yes. The repository includes test/test-symlink-security.js which creates realistic symlink attack scenarios and asserts that validatePath blocks them. These tests should be run using npm run test:test-symlink-security or node test/test-symlink-security.js to verify that directory traversal protection remains effective across code changes.
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 →