CherryHQ/cherry-studio · error · Error
Path traversal detected: target path must be direct child of
Error message
Path traversal detected: target path must be direct child of base directory
What it means
Thrown by ensurePathWithin() when the resolved target path's parent directory is not exactly the base directory. Unlike assertZipEntriesWithin (which allows nested subdirectories), this function requires the target to be a DIRECT child — no subdirectories are permitted. It is used exclusively for staging, backup, and final MCP package installation directories that must sit directly under the MCP root directory.
Source
Thrown at src/main/ai/mcp/McpPackageService.ts:26
const logger = loggerService.withContext('McpPackageService')
/**
* Ensure a target path is within the base directory to prevent path traversal attacks.
* This is the correct approach: validate the final resolved path rather than sanitizing input.
*
* @param basePath - The base directory that the target must be within
* @param targetPath - The target path to validate
* @returns The resolved target path if valid
* @throws Error if the target path escapes the base directory
*/
export function ensurePathWithin(basePath: string, targetPath: string): string {
const resolvedBase = path.resolve(basePath)
const resolvedTarget = path.resolve(path.normalize(targetPath))
// Must be direct child of base directory, no subdirectories allowed
if (path.dirname(resolvedTarget) !== resolvedBase) {
throw new Error('Path traversal detected: target path must be direct child of base directory')
}
return resolvedTarget
}
/**
* Guard against zip-slip: `node-stream-zip` writes each entry at `path.join(baseDir, entry.name)`
* with no containment check, so a name like `../../../foo` would escape `baseDir`. Reject any entry
* whose resolved destination is outside `baseDir` before extraction. Unlike {@link ensurePathWithin},
* nested subdirectories are allowed (a DXT archive legitimately contains them).
*
* @throws Error if any entry name escapes `baseDir`
*/
export function assertZipEntriesWithin(entryNames: string[], baseDir: string): void {
const root = path.resolve(baseDir)
for (const name of entryNames) {
const dest = path.resolve(baseDir, name)
if (dest !== root && !dest.startsWith(root + path.sep)) {View on GitHub (pinned to 726446b54c)
Solutions
- Sanitize serverDirName to remove path separators and '..' segments before calling ensurePathWithin.
- Generate the directory name from a UUID or slugified package name rather than trusting manifest input.
- Inspect the manifest's name/version fields that feed into serverDirName to confirm they don't contain path characters.
Example fix
// before const serverDir = ensurePathWithin(this.mcpDir, path.join(this.mcpDir, serverDirName)) // after — sanitize the directory name first const safeName = serverDirName.replace(/[/\\]/g, '_') const serverDir = ensurePathWithin(this.mcpDir, path.join(this.mcpDir, safeName))
Defensive patterns
Strategy: validation
Validate before calling
import path from 'node:path'
function sanitizeDirName(name: string): string {
// Remove path separators and traversal sequences
return name.replace(/[/\\]/g, '_').replace(/\.+/g, '.')
}
const safeName = sanitizeDirName(serverDirName)
const target = ensurePathWithin(mcpDir, path.join(mcpDir, safeName)) Type guard
function isDirectChildPath(basePath: string, targetPath: string): boolean {
const resolvedBase = path.resolve(basePath)
const resolvedTarget = path.resolve(path.normalize(targetPath))
return path.dirname(resolvedTarget) === resolvedBase
} Prevention
- Sanitize directory names derived from manifest input — strip path separators and '..' segments.
- Generate directory names from UUIDs or slugified package names instead of trusting manifest-provided names.
- Test ensurePathWithin with adversarial inputs (../, absolute paths, mixed separators) before relying on it.
When it happens
Trigger: Called from McpPackageService during package installation: staging directories (line 429-430), final extract directory (line 570), and server directory (line 645). Fails when the server directory name contains path separators or '..' sequences that would place the target outside or deeper than the immediate child level of mcpDir.
Common situations: A malicious or malformed MCP package manifest specifies a server directory name containing path separators (e.g., 'server/../../escape'); the serverDirName was derived from untrusted user input without sanitization; a platform-specific path separator in the directory name caused cross-OS validation failure.
Related errors
- Unsafe DXT entry path (zip-slip): ${name}
- Invalid command: path traversal detected in "${command}"
- Invalid args: path traversal detected in argument at index $
- Invalid MCP package upload: file name cannot contain path se
- Access denied: Path is outside the configured workspace root
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/1941cb439de846b9.
Report an issue: GitHub.