# Guest-Path Translation for Tool Permissions in Claude Desktop Cowork Mode

> Learn how Claude Desktop Debian uses guest-path translation to secure tool permissions and prevent directory-traversal attacks in Cowork Mode.

- Repository: [Aaddrick/claude-desktop-debian](https://github.com/aaddrick/claude-desktop-debian)
- Tags: internals
- Published: 2026-04-19

---

**The Claude Desktop Debian package implements guest-path translation in [`scripts/cowork-vm-service.js`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js) to convert VM-internal paths like `/sessions/1234/mnt/.auto-memory` into host-resolvable paths before granting tool permissions, preventing directory-traversal attacks while maintaining functional isolation.**

When running Claude Desktop in Cowork mode, the Electron frontend spawns tools inside an isolated VM backend. These tools receive arguments containing **guest-side paths** that the host filesystem cannot access directly. The `aaddrick/claude-desktop-debian` repository solves this through a dedicated translation layer that maps guest paths to host paths before the daemon spawns any process.

## Why Guest-Path Translation Matters

The Cowork VM backend isolates user code for security, but this isolation creates a path mismatch. When the frontend requests tool permissions like `Node(/sessions/abc123/mnt/.auto-memory)`, the VM-internal path `/sessions/...` does not exist on the host filesystem.

Without translation, the spawned tool would receive invalid paths, causing permission failures or exposing the host to path-traversal vulnerabilities if unvalidated guest input reaches the filesystem. The translation layer ensures every path is validated, resolved, and confined to authorized mount points before execution.

## Core Translation Functions in cowork-vm-service.js

The translation logic resides in [`scripts/cowork-vm-service.js`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js) and consists of three coordinated functions that handle different stages of argument sanitization.

### translateGuestPath(): Single Path Resolution

`translateGuestPath(guestPath, mountMap)` converts a single guest path to its host equivalent. This function validates that the path starts with `/sessions/`, extracts the mount name (e.g., `.auto-memory`), and looks up the corresponding host directory in `mountMap`.

Key implementation details from lines 162‑196:
- Validates the `/sessions/` prefix to ensure the path originates from the guest VM
- Extracts the mount name and any trailing sub-path
- Checks `mountMap` for the mount name, with fallback checks for `.${name}` and stripped leading dots for compatibility
- Joins the host base with the sub-path, resolves it, and blocks path-traversal attempts outside the mount
- Returns the normalized host path or `null` on failure

### translateEmbeddedGuestPaths(): CSV Tool List Processing

`translateEmbeddedGuestPaths(csv, mountMap)` handles tool-permission CSV strings like `Tool(path)` that appear in flags such as `--allowedTools` or `--disallowedTools`. This function scans the CSV, identifies guest paths inside parentheses, and translates them while preserving the surrounding tool names.

Implementation from lines 382‑406:
- Splits the CSV using `splitToolList`
- For each `Tool(pattern)` entry, normalizes leading slashes and checks for the `/sessions/` prefix
- Calls `translateGuestPath` for valid guest paths
- Rebuilds entries as `Tool(hostPath)` on success, or drops them on failure
- Returns the rebuilt CSV string with only valid, translated paths

### cleanSpawnArgs(): Full Argument Sanitization

`cleanSpawnArgs(rawArgs, mountMap)` produces the final sanitized argument vector for the spawned tool. This function iterates over `rawArgs` and applies different translation strategies based on flag types.

Implementation from lines 418‑452:
- Detects single-path flags like `--add-dir` and `--plugin-dir`, calling `translateGuestPath` and discarding the flag/value pair if translation fails
- Detects tool-list flags like `--allowedTools` and `--disallowedTools`, delegating to `translateEmbeddedGuestPaths` to remove only invalid entries while keeping the flag
- Handles plugin directory resolution via `resolvePluginRoot` for `--plugin-dir` specifically

## Building the Mount Map

The translation functions depend on `mountMap`, which is constructed in `buildMountMap` (around line 221) immediately before spawning a tool. This map combines:

- **Mount binds**: Explicit host-to-guest bindings supplied by the user
- **Additional mounts**: Front-end requested mounts like `.auto-memory` and `.skills`

`buildMountMap` validates that every resolved host path remains inside the user’s `$HOME` directory, rejecting any mount that would escape the home folder. This validation prevents the VM from accessing sensitive system paths even if the frontend requests them.

## Security Safeguards

The guest-path translation layer implements multiple security controls to prevent sandbox escapes:

**Path Traversal Prevention**: `translateGuestPath` resolves the final joined path and verifies it remains within the authorized mount base directory. Any attempt to traverse upward with `..` sequences results in the function returning `null` and the path being dropped from arguments.

**Prefix Validation**: All guest paths must begin with `/sessions/` to be considered valid. This prevents the frontend from injecting arbitrary host paths directly.

**Mount Scope Enforcement**: `buildMountMap` restricts all mounts to the user’s home directory, ensuring the VM cannot access system directories like `/etc` or `/usr` even through indirect translation.

**Silent Failure for Invalid Paths**: Rather than crashing or passing through unvalidated paths, the translation functions return `null` or drop entries. This ensures tools only receive valid, resolvable host paths.

## Practical Examples

The following examples demonstrate how the translation functions process real-world inputs.

### Translating a CSV of Tool Permissions

```javascript
// Example CSV coming from the front-end:
const csv = 'Git,Node(/sessions/abc123/mnt/.auto-memory),Shell';

// Assume a mount map where ".auto-memory" points to the host's ~/.auto-memory
const mountMap = {
  '.auto-memory': '/home/you/.auto-memory',
  '.skills': '/home/you/.skills',
};

const translated = translateEmbeddedGuestPaths(csv, mountMap);
console.log(translated);
// → "Git,Node(/home/you/.auto-memory),Shell"

```

The function drops any entry whose guest path cannot be resolved, preventing a tool from receiving an impossible permission rule.

### Cleaning a Full Argument List for a Tool Spawn

```javascript
const rawArgs = [
  '--add-dir', '/sessions/abc123/mnt/.skills',
  '--allowedTools', 'Git,Node(/sessions/abc123/mnt/.auto-memory),Shell',
  '--plugin-dir', '/sessions/abc123/mnt/.plugins',
];

const clean = cleanSpawnArgs(rawArgs, mountMap);
console.log(clean);
/*
[
  '--add-dir', '/home/you/.skills',
  '--allowedTools', 'Git,Node(/home/you/.auto-memory),Shell',
  '--plugin-dir', '/home/you/.plugins'   // resolved via resolvePluginRoot inside cleanSpawnArgs
]
*/

```

If any path cannot be mapped, the corresponding flag/value pair is silently omitted, ensuring the spawned process never receives an invalid path.

### End-to-End Spawn Request (Simplified)

```javascript
// Simulated request payload from the Electron UI
const request = {
  cwd: '/sessions/abc123/mnt/.skills/project',
  args: ['--add-dir', '/sessions/abc123/mnt/.skills', '--allowedTools', 'Node(/sessions/abc123/mnt/.auto-memory)'],
  env: { CLAUDE_CONFIG_DIR: '/sessions/abc123/mnt/.config' },
  additionalMounts: { '.auto-memory': { path: '.auto-memory' }, '.skills': { path: '.skills' } },
  mountBinds: []   // none in this simple case
};

// 1️⃣ Build mount map
const mountMap = buildMountMap(request.additionalMounts, request.mountBinds);

// 2️⃣ Resolve working directory
const workDir = resolveWorkDir(request.cwd, null, mountMap);

// 3️⃣ Clean args & env
const cleanArgs = cleanSpawnArgs(request.args, mountMap);
const mergedEnv = buildSpawnEnv(request.env, mountMap);

// 4️⃣ Finally spawn (pseudo-code)
spawnTool({ cwd: workDir, args: cleanArgs, env: mergedEnv });

```

Every step leverages the translation helpers, guaranteeing that the child process only sees host-side paths.

## Summary

- **Guest-path translation** converts VM-internal paths like `/sessions/.../mnt/.auto-memory` to host-resolvable paths before spawning tools in Claude Desktop's Cowork mode.
- The translation logic lives in [`scripts/cowork-vm-service.js`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js), specifically in `translateGuestPath`, `translateEmbeddedGuestPaths`, and `cleanSpawnArgs`.
- **Security controls** include path-traversal blocking, `/sessions/` prefix validation, and home-directory scope enforcement via `buildMountMap`.
- The system silently drops invalid paths rather than passing them through, ensuring tools only receive valid host-side resources.
- Enable `COWORK_VM_DEBUG` to inspect translation decisions during permission failures.

## Frequently Asked Questions

### How does Claude Desktop prevent path traversal attacks during guest-path translation?

The `translateGuestPath` function in [`scripts/cowork-vm-service.js`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js) resolves the final joined path and verifies it remains within the authorized mount base directory. If the resolved path escapes the mount root via `..` sequences or symbolic links, the function returns `null` and the path is dropped from the tool's arguments. Additionally, `buildMountMap` restricts all mounts to the user's `$HOME` directory, preventing access to system paths like `/etc` or `/usr`.

### What happens if a guest path cannot be translated to a host path?

Rather than crashing or passing the invalid path through, the translation functions implement silent failure. `translateGuestPath` returns `null` for unmappable paths. When processing CSV tool lists via `translateEmbeddedGuestPaths`, entries containing invalid guest paths are removed entirely from the output string. In `cleanSpawnArgs`, failure to translate a single-path flag like `--add-dir` results in the entire flag-value pair being omitted from the final argument vector.

### Which environment variables and arguments undergo guest-path translation?

The translation pipeline processes several specific parameters in [`scripts/cowork-vm-service.js`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js). Argument flags including `--add-dir`, `--plugin-dir`, `--allowedTools`, and `--disallowedTools` are scanned for guest paths. Environment variables `CLAUDE_CONFIG_DIR` and `CLAUDE_COWORK_MEMORY_PATH_OVERRIDE` are processed by `buildSpawnEnv` to translate any embedded guest paths. The working directory specified in spawn requests is also resolved via `resolveWorkDir`, which may invoke `translateGuestPath` for guest-relative paths.

### Where can I find the test suite for guest-path translation logic?

The Bats test suite for validating translation functions is located at `tests/cowork-path-translation.bats` in the repository. This file contains test cases covering various mount map configurations, edge cases for path traversal attempts, and validation of the CSV parsing logic used in `translateEmbeddedGuestPaths`. Running these tests ensures that modifications to [`scripts/cowork-vm-service.js`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js) maintain the security and functional guarantees of the translation layer.