{"record":{"id":"c7c26cb1c66206a2","repo":"CherryHQ/cherry-studio","slug":"unsafe-dxt-entry-path-zip-slip-name","errorCode":null,"errorMessage":"Unsafe DXT entry path (zip-slip): ${name}","messagePattern":"Unsafe DXT entry path \\(zip-slip\\): (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/McpPackageService.ts","lineNumber":45,"sourceCode":"  }\n\n  return resolvedTarget\n}\n\n/**\n * Guard against zip-slip: `node-stream-zip` writes each entry at `path.join(baseDir, entry.name)`\n * with no containment check, so a name like `../../../foo` would escape `baseDir`. Reject any entry\n * whose resolved destination is outside `baseDir` before extraction. Unlike {@link ensurePathWithin},\n * nested subdirectories are allowed (a DXT archive legitimately contains them).\n *\n * @throws Error if any entry name escapes `baseDir`\n */\nexport function assertZipEntriesWithin(entryNames: string[], baseDir: string): void {\n  const root = path.resolve(baseDir)\n  for (const name of entryNames) {\n    const dest = path.resolve(baseDir, name)\n    if (dest !== root && !dest.startsWith(root + path.sep)) {\n      throw new Error(`Unsafe DXT entry path (zip-slip): ${name}`)\n    }\n  }\n}\n\ninterface BaseMcpPackageManifest {\n  name: string\n  display_name?: string\n  version: string\n  description?: string\n  long_description?: string\n  author?: {\n    name?: string\n    email?: string\n    url?: string\n  }\n  repository?: {\n    type?: string\n    url?: string","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/McpPackageService.ts#L27-L63","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Do not install the archive — it is either malicious or malformed. Report the offending entry name to the package author.","If building a DXT/MCPB archive, ensure all entry paths are relative and do not contain '..' segments.","Re-package the archive with a tool that normalizes entry paths (e.g., zip with relative paths only)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import path from 'node:path'\n\nfunction isSafeZipEntry(name: string, baseDir: string): boolean {\n  const root = path.resolve(baseDir)\n  const dest = path.resolve(baseDir, name)\n  return dest === root || dest.startsWith(root + path.sep)\n}\n\n// Pre-check before calling assertZipEntriesWithin\nconst unsafe = entryNames.filter(name => !isSafeZipEntry(name, baseDir))\nif (unsafe.length > 0) {\n  logger.error('Refusing to extract archive with unsafe entries', { unsafe })\n}","typeGuard":null,"tryCatchPattern":"try {\n  assertZipEntriesWithin(Object.keys(await zip.entries()), tempExtractDir)\n} catch (error) {\n  if (error instanceof Error && error.message.includes('zip-slip')) {\n    logger.error('Refusing to install package — archive contains unsafe entry paths', { error: error.message })\n    // Clean up the temp extraction directory and abort installation\n    throw new Error('Package archive is unsafe (zip-slip detected). Do not install.')\n  }\n  throw error\n}","preventionTips":["Only install DXT/MCPB packages from trusted sources or verified registries.","Scan archive entry names before extraction — assertZipEntriesWithin is the guard, but logging the offender helps triage.","When building archives, use relative paths only and avoid '..' segments in entry names."],"tags":["security","zip-slip","archive","mcp","package-install","path-traversal"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}