{"record":{"id":"eae43e674d19dbad","repo":"CherryHQ/cherry-studio","slug":"path-is-outside-the-workspace-userpath","errorCode":null,"errorMessage":"Path is outside the workspace: ${userPath}","messagePattern":"Path is outside the workspace: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/channels/security/WorkspaceFileGuard.ts","lineNumber":49,"sourceCode":"    // waste retries on other paths.\n    if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {\n      throw new Error(`Session workspace is unavailable: ${workspaceRoot}`)\n    }\n    throw error\n  }\n\n  let realTarget: string\n  try {\n    realTarget = await realpath(requested)\n  } catch (error) {\n    if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {\n      throw new Error(`File not found in workspace: ${userPath}`)\n    }\n    throw error\n  }\n\n  if (realTarget !== realRoot && !realTarget.startsWith(realRoot + path.sep)) {\n    throw new Error(`Path is outside the workspace: ${userPath}`)\n  }\n\n  return readCanonicalLocalFile(requested, realTarget, userPath)\n}\n","sourceCodeStart":31,"sourceCodeEnd":54,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/channels/security/WorkspaceFileGuard.ts#L31-L54","documentation":"Thrown by resolveWorkspaceFile() as a security containment check. After resolving both workspaceRoot and the requested path through realpath (which follows symlinks), it verifies the target's real path starts with realRoot + path.sep. If it does not, the path — after symlink resolution — escapes the workspace boundary. This is defense-in-depth against traversal mistakes and prompt-injection path selection, not a sandbox against code-executing agents.","triggerScenarios":"Called when an agent supplies a path containing '../' sequences that resolve outside the workspace, or when a symlink inside the workspace points to a target outside the workspace. The check fires after realpath resolves everything, so even an obfuscated path like a symlink chain that eventually escapes will be caught.","commonSituations":"An agent following a prompt-injection instruction tried to read '../../../etc/passwd' or an absolute path outside the workspace; a symlink in the workspace points to /etc or the user's home directory; the agent was given a workspace-adjacent path that resolves to a sibling directory outside the root.","solutions":["Use only paths relative to the workspace root without '../' sequences.","If the path legitimately needs to be outside the workspace, use resolveLocalFile() instead (which does no containment check) — but understand this removes the security boundary.","Audit workspace symlinks: remove or reject symlinks that point outside the workspace root."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import path from 'node:path'\n\nfunction isPathWithinWorkspace(workspaceRoot: string, userPath: string): boolean {\n  const resolved = path.resolve(workspaceRoot, userPath)\n  return resolved === workspaceRoot || resolved.startsWith(workspaceRoot + path.sep)\n}\n\nif (!isPathWithinWorkspace(workspaceRoot, userPath)) {\n  throw new Error(`Path '${userPath}' escapes the workspace — use a workspace-relative path without '../'`)\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await resolveWorkspaceFile(workspaceRoot, userPath)\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Path is outside the workspace:')) {\n    // Security violation — log and reject, do not retry with alternative paths\n    logger.warn('Path escape attempt blocked', { userPath, workspaceRoot })\n    throw error\n  }\n  throw error\n}","preventionTips":["Always use paths relative to the workspace root — avoid absolute paths and '../' sequences.","Audit workspace symlinks and remove any that point outside the workspace boundary.","Treat this error as a security signal — investigate if it appears in agent logs, as it may indicate prompt injection."],"tags":["security","filesystem","path-traversal","workspace","symlink","channels"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}