# Security Implications of `allowedDirectories` in Desktop Commander MCP: A Complete Guide

> Understand the security implications of allowedDirectories in Desktop Commander MCP. Learn how to enforce least privilege isolation and prevent unauthorized access to your filesystem.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: best-practices
- Published: 2026-08-05

---

**The `allowedDirectories` whitelist in Desktop Commander MCP determines which filesystem paths an AI assistant can access, with an empty array defaulting to full system access and explicit paths enforcing least-privilege isolation.**

Desktop Commander MCP implements a **path-based access control model** that gates every file operation—read, write, move, and search—behind a configurable whitelist. This article examines the security architecture of `allowedDirectories` as implemented in `wonderwhy-er/DesktopCommanderMCP`, including how the whitelist is defined, enforced, and what risks emerge from common misconfigurations.

## How `allowedDirectories` Is Defined and Initialized

The whitelist originates in two configuration files with conflicting defaults that create a security-relevant transition state.

### Configuration Schema and UI Labeling

The user-facing field appears as **"Allowed Folders"** in the configuration interface. The schema description explicitly warns about the security trade-off:

```ts
// src/config-field-definitions.ts
allowedDirectories: {
  label: 'Allowed Folders',
  description: 'These are the folders Desktop Commander is allowed to read and edit. Think of this as a permission list. Keeping it small is safer. If this list is empty, Desktop Commander can access your entire filesystem.',
  valueType: 'array',
}

```

This definition establishes `allowedDirectories` as an **array of path strings** with security-critical semantics.

### Default Value vs. Runtime Fallback

The configuration manager initializes new configs with an empty array:

```ts
// src/config-manager.ts – default config
allowedDirectories: [],                // ← empty = full‑filesystem access

```

However, `getAllowedDirs()` in the filesystem tools implements a **runtime fallback** that modifies this behavior:

```ts
// src/tools/filesystem.ts
async function getAllowedDirs(): Promise<string[]> {
  const config = await configManager.getConfig();
  if (config.allowedDirectories && Array.isArray(config.allowedDirectories)) {
    allowedDirectories = config.allowedDirectories;
  } else {
    allowedDirectories = [os.homedir()];  // Fallback to home directory
    await configManager.setValue('allowedDirectories', allowedDirectories);
  }
  return allowedDirectories;
}

```

This means:
- An **explicit empty array** (`[]`) in config → no restrictions
- A **missing or malformed value** → auto-constrained to home directory
- Any **populated array** → enforces explicit whitelist

## Path Validation Architecture: `isPathAllowed()`

The core security enforcement occurs in `isPathAllowed()`, called by `validatePath()` before every filesystem operation.

### Whitelist Evaluation Logic

```ts
// src/tools/filesystem.ts
async function isPathAllowed(pathToCheck: string): Promise<boolean> {
  const allowedDirectories = await getAllowedDirs();
  
  // Bypass conditions: "/" or empty array grants full access
  if (allowedDirectories.includes('/') || allowedDirectories.length === 0) {
    return true;
  }
  
  const isAllowed = allowedDirectories.some(allowedDir => {
    const normAllowed = normalizePath(allowedDir);
    const normCheck   = normalizePath(pathToCheck);
    
    if (normCheck === normAllowed) return true;
    
    // Sub-directory check with separator to prevent prefix collisions
    if (normCheck.startsWith(normAllowed + path.sep)) return true;
    
    // Windows drive root special case
    if (normAllowed === 'c:' && process.platform === 'win32')
      return normCheck.startsWith('c:');
      
    return false;
  });
  
  return isAllowed;
}

```

The implementation addresses several attack vectors:

- **Prefix collision mitigation**: Adding `path.sep` ensures `/home/user` does not match `/home/userdata`
- **Path normalization**: Resolves `.`, `..`, and duplicate separators before comparison
- **Platform handling**: Special-cases Windows drive letters

### Enforcement Failure Mode

When validation fails, the error reveals the whitelist contents (potentially information-leaking sensitive paths):

```ts
if (!(await isPathAllowed(pathForNextCheck))) {
  throw new Error(
    `Path not allowed: ${requestedPath}. Must be within one of these directories: ${allowedDirectories.join(', ')}`);
}

```

## Critical Security Scenarios

| Configuration | Security Posture | Risk Level |
|-------------|----------------|-----------|
| `[]` (empty array) | **Full filesystem access** — any path readable/writable | **Critical** |
| `['/']` (Unix root) | Functionally equivalent to empty array on Unix | **Critical** |
| `['C:']` (Windows root) | Only C: drive root, *not* subdirectories — misleading | **High** |
| `[os.homedir()]` | Home directory and all descendants | **Medium** |
| `['/home/user/project']` | Single project isolation — **recommended** | **Low** |

### The Empty Array Default: A Backwards-Compatibility Risk

The default `allowedDirectories: []` prioritizes functionality over security. Per the source comment in [`config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config-manager.ts), this preserves behavior for existing users, but new installations inherit **unrestricted access** until explicitly configured.

The `set_config_value` tool documentation marks this as **destructive**:

> "Setting allowedDirectories to an empty array grants full access to the entire file system."

## Symlink and Path Traversal Defenses

`validatePath()` implements multi-layer protections in [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts):

1. **Symlink resolution**: `fs.realpath()` resolves symbolic links before whitelist checking
2. **Path normalization**: Collapses navigation sequences that could escape boundaries
3. **Directory traversal blocking**: The `startsWith(normAllowed + path.sep)` check prevents `../` escapes

The test suite [`test/test-allowed-directories.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/test/test-allowed-directories.js) verifies these protections, including a dedicated **prefix blocking test** (lines 308-353) confirming that sibling directories with overlapping names are correctly rejected.

## Practical Configuration Examples

### Restrict to Single Project (Recommended)

```json
{
  "name": "set_config_value",
  "arguments": {
    "key": "allowedDirectories",
    "value": ["/home/you/projects/my-app"]
  }
}

```

Result: Any access outside `/home/you/projects/my-app` produces:

```

Path not allowed: /etc/passwd. Must be within one of these directories: /home/you/projects/my-app

```

### Verify Current Whitelist

```json
{
  "name": "get_config",
  "arguments": {}
}

```

Inspect the `allowedDirectories` array before granting sensitive operations.

### Multiple Isolated Directories

```json
{
  "name": "set_config_value",
  "arguments": {
    "key": "allowedDirectories",
    "value": [
      "/home/you/work/project-a",
      "/home/you/work/project-b"
    ]
  }
}

```

Directories remain isolated—`project-a` cannot access `project-b` files.

## Key Security Files in Desktop Commander MCP

| File | Security Function |
|------|-------------------|
| [`src/config-field-definitions.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-field-definitions.ts) | Schema definition and user-facing description of `allowedDirectories` |
| [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts) | Default value (`[]`) and configuration persistence |
| [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts) | `getAllowedDirs()`, `isPathAllowed()`, `validatePath()` — the enforcement engine |
| [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) | Tool documentation embedding whitelist warnings |
| [`test/test-allowed-directories.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/test/test-allowed-directories.js) | Automated verification of boundary conditions and attack scenarios |

## Summary

- **`allowedDirectories` is the single security boundary** for all filesystem operations in Desktop Commander MCP
- **Empty array = no restrictions**: The default configuration grants full system access for backwards compatibility
- **Explicit paths enforce least privilege**: Each filesystem call validates against the resolved, normalized path before execution
- **Multiple defensive layers**: Symlink resolution, path normalization, and separator-aware prefix checking prevent common escape techniques
- **Configuration is mutable at runtime**: Use `set_config_value` to tighten restrictions without restart, but recognize changes apply prospectively

## Frequently Asked Questions

### What happens if I leave `allowedDirectories` empty?

The assistant gains **unrestricted read/write access** to your entire filesystem. This is the default configuration in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts) for backwards compatibility, but it exposes system files, SSH keys, and sensitive documents. You should explicitly restrict this list before handling confidential data.

### Can the assistant escape an allowed directory using `../` or symlinks?

No. The `validatePath()` function in [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts) resolves symlinks via `fs.realpath()` and normalizes all paths before whitelist checking. The `isPathAllowed()` implementation specifically uses `path.sep` suffix matching to prevent `/home/user` from matching `/home/userdata`, blocking both traversal and prefix collision attacks.

### Why does `['/']` behave differently than `['C:']` on Windows?

On Unix systems, `'/'` represents the filesystem root and grants full access (equivalent to an empty array). On Windows, `'C:'` only matches the drive root itself due to the special-case handler in `isPathAllowed()` — subdirectories like `C:\Users` would be rejected. This platform inconsistency makes explicit directory names safer than root entries.

### How do I check what directories are currently allowed?

Call the `get_config` tool with no arguments, or inspect the `allowedDirectories` field in the returned configuration. The array contents are displayed directly, though error messages from rejected operations also leak the whitelist for debugging purposes.