CherryHQ/cherry-studio · error · Error

Unsafe DXT entry path (zip-slip): ${name}

Error message

Unsafe DXT entry path (zip-slip): ${name}

What it means

Thrown by assertZipEntriesWithin() when a DXT or MCPB archive entry name resolves to a path outside the extraction base directory. This is a zip-slip guard: node-stream-zip writes each entry at path.join(baseDir, entry.name) with no built-in containment check, so a crafted entry name like '../../../etc/cron.d/evil' would escape baseDir. Unlike ensurePathWithin, nested subdirectories are allowed (DXT archives legitimately contain them).

Source

Thrown at src/main/ai/mcp/McpPackageService.ts:45

  }

  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)) {
      throw new Error(`Unsafe DXT entry path (zip-slip): ${name}`)
    }
  }
}

interface BaseMcpPackageManifest {
  name: string
  display_name?: string
  version: string
  description?: string
  long_description?: string
  author?: {
    name?: string
    email?: string
    url?: string
  }
  repository?: {
    type?: string
    url?: string

View on GitHub (pinned to 726446b54c)

Solutions

  1. Do not install the archive — it is either malicious or malformed. Report the offending entry name to the package author.
  2. If building a DXT/MCPB archive, ensure all entry paths are relative and do not contain '..' segments.
  3. Re-package the archive with a tool that normalizes entry paths (e.g., zip with relative paths only).
Defensive patterns

Strategy: validation

Validate before calling

import path from 'node:path'

function isSafeZipEntry(name: string, baseDir: string): boolean {
  const root = path.resolve(baseDir)
  const dest = path.resolve(baseDir, name)
  return dest === root || dest.startsWith(root + path.sep)
}

// Pre-check before calling assertZipEntriesWithin
const unsafe = entryNames.filter(name => !isSafeZipEntry(name, baseDir))
if (unsafe.length > 0) {
  logger.error('Refusing to extract archive with unsafe entries', { unsafe })
}

Try / catch

try {
  assertZipEntriesWithin(Object.keys(await zip.entries()), tempExtractDir)
} catch (error) {
  if (error instanceof Error && error.message.includes('zip-slip')) {
    logger.error('Refusing to install package — archive contains unsafe entry paths', { error: error.message })
    // Clean up the temp extraction directory and abort installation
    throw new Error('Package archive is unsafe (zip-slip detected). Do not install.')
  }
  throw error
}

Prevention

When it happens

Trigger: Called from McpPackageService at line 521 after opening a zip and reading its entry list. Every entry name is resolved against baseDir and checked: if the resolved destination is not the root itself and does not start with root + path.sep, the extraction is aborted before any file is written.

Common situations: A malicious DXT/MCPB archive contains entries with '../' sequences designed to overwrite system files or inject code outside the MCP directory; a poorly packaged archive with absolute entry paths (e.g., '/etc/config'); an archive created on Windows with backslash separators that resolve unexpectedly on a Unix host.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/c7c26cb1c66206a2. Report an issue: GitHub.