# Symlink Traversal Prevention in Desktop Commander: How the MCP Server Blocks Directory Escape Attacks

> Learn how Desktop Commander MCP Server prevents symlink traversal attacks by validating resolved symbolic links against an allowlist of directories. Secure your file operations today.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: how-to-guide
- Published: 2026-07-31

---

**Desktop Commander prevents symlink traversal attacks by resolving every symbolic link in a requested path—whether the final file exists or not—and validating the canonicalized result against a whitelist of allowed directories before executing any file operation.**

Desktop Commander is a Model Context Protocol (MCP) server that exposes file-system operations to AI agents, making robust **symlink traversal prevention** essential for maintaining sandbox security. The server confines all read, write, and directory operations to trusted paths by canonicalizing inputs through the `validatePath` function in [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts). This ensures that malicious symbolic links pointing to sensitive system locations—such as a symlink to `/etc/passwd` placed inside an allowed folder—are detected and rejected before any data is accessed or modified.

## How the Security Mechanism Works

The core defense resides in the `validatePath` function (lines 44–106 of [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts)). Every public file-system API—including `readFile`, `writeFile`, and `createDirectory`—invokes this validator before interacting with the underlying operating system.

### Canonicalization and Symlink Resolution

The validation process begins by expanding the tilde (`~`) character and converting relative paths to absolute form using `expandHome` (lines 38–42) and `normalizePath` (lines 34–36). The critical security logic then distinguishes between existing and non-existing targets:

- **For existing paths**, the code calls `fs.realpath`, which automatically follows all symbolic links in the path chain and returns the true canonical location.
- **For new paths**, the implementation performs a "SECURITY FIX" (lines 58–86): it resolves the nearest existing ancestor directory using `fs.realpath`, then re-attaches the non-existent leaf components. This guarantees that symlinks anywhere in the parent chain are revealed even when the final file does not yet exist.

### Whitelist Verification with isPathAllowed

After resolving the canonical path, `validatePath` delegates to `isPathAllowed` (lines 71–115) to enforce boundary checks. This function normalizes both the resolved path and each entry in the `allowedDirectories` configuration (retrieved via `getAllowedDirs` at lines 11–25), then verifies:

- Exact equality with an allowed directory
- Sub-directory containment (ensuring `/home/user` does not erroneously match `/home/username`)
- Windows drive-letter normalization for cross-platform compatibility

If the resolved path falls outside the permitted set, the function throws a validation error and emits a `server_path_validation_error` telemetry event via the capture utility for security monitoring.

### Race Condition Mitigation

Because symlink resolution occurs **before** any `fs.stat`, `fs.readFile`, or `fs.mkdir` call, the server eliminates time-of-check to time-of-use (TOCTOU) vulnerabilities. An attacker cannot swap a legitimate file for a malicious symlink after validation but before execution, as the canonical path is computed once and passed immutably to all downstream operations.

## Source Code Implementation Details

| Component | Purpose | Location |
|-----------|---------|----------|
| **validatePath** | Central validation function that resolves symlinks and enforces directory constraints. | [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts), L44–106 |
| **isPathAllowed** | Whitelist comparison logic with platform-specific normalization. | [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts), L71–115 |
| **getAllowedDirs** | Retrieves permitted directories from `configManager.getConfig()` or defaults to the home directory. | [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts), L11–25 |
| **normalizePath** | Converts paths to absolute form and handles case-insensitive normalization. | [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts), L34–36 |
| **expandHome** | Expands the `~` alias to the user's home directory. | [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts), L38–42 |

## Practical Usage Examples

### Reading a File Safely

When calling `readFile`, the symlink traversal prevention executes automatically behind the scenes:

```typescript
import { readFile } from './tools/filesystem.js';

(async () => {
  try {
    const result = await readFile('~/projects/notes.txt');
    console.log('Content:', result.content);
  } catch (e) {
    console.error('Access denied:', e.message);
  }
})();

```

Even if `~/projects` contains a malicious symlink to `/etc`, the `validatePath` call inside `readFileFromDisk` resolves the true location and rejects the operation if it escapes the allowed boundaries.

### Writing to a New File with Parent Directory Validation

Creating new files triggers the parent-directory resolution security fix:

```typescript
import { writeFile } from './tools/filesystem.js';

// Attempting to write through a symlink attack
await writeFile('~/projects/malicious-link/target.txt', 'malicious content', 'rewrite');

```

If `~/projects/malicious-link` is a symlink pointing to `/etc/passwd`, the validator resolves the parent directory (`~/projects`) via `fs.realpath`, discovers the symlink destination (`/etc/passwd`), and rejects the operation because `/etc` is not in the `allowedDirectories` whitelist.

### Creating Nested Directories Safely

The protection extends to directory creation operations:

```typescript
import { createDirectory } from './tools/filesystem.js';

await createDirectory('~/projects/subdir/nested/deep');

```

Although `subdir/nested/deep` does not exist, `validatePath` walks up the tree to the nearest existing ancestor, resolves any symlinks encountered during the ascent, and only proceeds with `mkdir` if the resolved canonical path remains within the trusted boundary.

### Moving Files with Dual Validation

Both source and destination paths undergo independent validation:

```typescript
import { moveFile } from './tools/filesystem.js';

await moveFile('~/projects/temp.txt', '~/archive/final.txt');

```

This ensures that neither the source nor the destination can exploit symlink traversal to read from or write to restricted system locations.

## Summary

- **Canonical resolution first**: Desktop Commander always resolves symlinks using `fs.realpath` before checking permissions, preventing attackers from hiding malicious destinations behind relative paths or symbolic links.
- **New file protection**: The "SECURITY FIX" logic (lines 58–86) specifically handles non-existent paths by resolving parent directories, closing the gap where creating new files might otherwise bypass checks.
- **Configurable boundaries**: The `allowedDirectories` whitelist in [`config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config-manager.ts) lets administrators define strict sandboxes, with all paths normalized for case-insensitive and cross-platform consistency.
- **Atomic validation**: By computing the canonical path once and reusing it for all subsequent operations, the server eliminates race conditions between validation and execution.
- **Telemetry integration**: Failed validation attempts trigger `server_path_validation_error` events, enabling security monitoring and audit trails for suspicious access patterns.

## Frequently Asked Questions

### What is a symlink traversal attack and why does it matter for MCP servers?

A symlink traversal attack occurs when an attacker places a symbolic link inside an allowed directory that points to a sensitive location outside the sandbox, such as `/etc/passwd` or `C:\Windows\System32`. When the application follows the link, it inadvertently accesses files outside its intended scope. For MCP servers like Desktop Commander that grant AI agents file-system access, this could allow unauthorized reading of credentials or modification of system files. The `validatePath` function prevents this by resolving all symlinks and verifying the final canonical location against a whitelist before any file operation executes.

### How does Desktop Commander handle symlinks when creating new files that don't exist yet?

When the target file does not exist, the server cannot use `fs.realpath` on the full path. Instead, it implements a security fix (lines 58–86 in [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts)) that finds the nearest existing ancestor directory, resolves that ancestor using `fs.realpath` to reveal any symlinks in the parent chain, then re-attaches the non-existent leaf components. This ensures that even if an attacker creates a symlink to a restricted directory and attempts to write a new file through it, the resolved canonical path is checked against `allowedDirectories` and rejected if it escapes the sandbox.

### What happens when a path fails validation?

If `isPathAllowed` determines that a resolved path falls outside the configured `allowedDirectories`, `validatePath` throws an error with the message "Path is outside allowed directories". Simultaneously, the server emits a `server_path_validation_error` telemetry event via the capture utility in [`src/utils/capture.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/capture.ts). The operation is aborted before any actual file system call (read, write, or directory creation) occurs, ensuring no data leakage or unauthorized modification takes place.

### Can I customize which directories Desktop Commander is allowed to access?

Yes, the whitelist is fully configurable through the [`config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config-manager.ts) module. The `getAllowedDirs` function retrieves the `allowedDirectories` array from the server configuration; if none are specified, it defaults to the user's home directory. Administrators can define multiple allowed roots, and the `isPathAllowed` function will permit operations within any of those directories or their subdirectories, provided the resolved canonical path matches exactly or begins with an allowed directory path followed by a path separator.