{"record":{"id":"97accc894f3bfec9","repo":"yamadashy/repomix","slug":"path-input-must-be-relative-to-workspace-root","errorCode":null,"errorMessage":"Path \"${input}\" must be relative to workspace root — no \"/\", \"~/\", \"../\", drive, or \"..\" segment. Use e.g. \"src/index.ts\" or \".\" for whole workspace.","messagePattern":"Path \"(.+?)\" must be relative to workspace root — no \"/\", \"~/\", \"\\.\\./\", drive, or \"\\.\\.\" segment\\. Use e\\.g\\. \"src/index\\.ts\" or \"\\.\" for whole workspace\\.","errorType":"validation","errorClass":"PathScopeError","httpStatus":null,"severity":"error","filePath":"src/mcp/pathScope.ts","lineNumber":65,"sourceCode":" * Rejected: absolute paths (\"/x\", \"C:\\x\", \"\\x\", UNC), \"~\" home refs, and any\n * \"..\" traversal segment (either separator). Symlink escapes are caught via\n * realpath. Returns the resolved absolute path (realpath when the target exists).\n */\nexport const resolveWithinRoot = async (\n  root: string,\n  input: string,\n  deps: { realpath: RealpathFn } = { realpath: (p) => fs.realpath(p) },\n): Promise<string> => {\n  const rootResolved = path.resolve(root);\n\n  if (input === '' || input === '.') {\n    return rootResolved;\n  }\n\n  // Reject anything that escapes the workspace root — absolute/drive/UNC, a \"~\"\n  // home ref, or any \"..\" segment. A redundant leading \"./\" or \".\\\" is allowed.\n  if (isEscapingPath(input)) {\n    throw new PathScopeError(\n      `Path \"${input}\" must be relative to workspace root — no \"/\", \"~/\", \"../\", drive, or \"..\" segment. Use e.g. \"src/index.ts\" or \".\" for whole workspace.`,\n    );\n  }\n\n  const candidate = path.resolve(rootResolved, input);\n\n  if (!isInside(rootResolved, candidate)) {\n    throw new PathScopeError(`Path \"${input}\" resolves outside workspace root.`);\n  }\n\n  // Resolve symlinks to catch a link inside root that points back out. If a path\n  // can't be resolved via realpath (e.g. the target does not exist yet, or the\n  // root itself can't be stat'd), fall back to the lexical path — it is already\n  // confined lexically above.\n  let realRoot: string;\n  try {\n    realRoot = (await deps.realpath(rootResolved)) as string;\n  } catch {","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/yamadashy/repomix/blob/f465ad909315a22120636baf03fa5e28701a50cb/src/mcp/pathScope.ts#L47-L83","documentation":"MCP path scoping: resolveWithinRoot rejects any input path that lexically escapes the workspace root — absolute paths, drive/UNC paths, '~/...' home refs, or any '..' segment — throwing PathScopeError before any fs access. This is a security guard keeping MCP tool calls confined to the workspace.","triggerScenarios":"An MCP tool call passes an absolute path ('/etc/passwd'), a home-relative path ('~/notes.txt'), a Windows drive path ('C:\\\\x'), or a path containing '..' to resolveWithinRoot.","commonSituations":"LLM agents constructing absolute paths from previous tool output; clients on Windows sending drive-qualified paths; callers assuming the MCP server accepts OS-wide paths like the CLI does.","solutions":["Pass workspace-root-relative paths, e.g. 'src/index.ts' or '.' for the whole workspace","Strip any leading home or drive prefix from the path before calling","Resolve the path against the workspace root yourself and re-submit the relative form","If the target genuinely lives outside the root, launch/restart the MCP server with that directory as the root"],"exampleFix":"// before\nresolveWithinRoot(root, '/home/me/project/src/index.ts')\n// after\nresolveWithinRoot(root, 'src/index.ts')","handlingStrategy":"validation","validationCode":"const isSafeRelative = (p: string) =>\n  p !== '' && !p.startsWith('/') && !p.startsWith('~') && !/^[a-zA-Z]:/.test(p) &&\n  !p.split(/[\\\\/]/).includes('..');\nif (!isSafeRelative(input)) throw new Error('path must be relative to workspace root');","typeGuard":null,"tryCatchPattern":"try {\n  const abs = await resolveWithinRoot(root, input);\n} catch (err) {\n  if (err instanceof PathScopeError) {\n    console.error(`Rejected path ${input}; send a workspace-relative path like src/index.ts.`);\n  } else throw err;\n}","preventionTips":["Have MCP clients normalize to root-relative paths before tool calls","Never forward raw absolute paths from previous tool results","Sanitize '~' and drive prefixes when bridging CLI and MCP usage","Document in tool schemas that paths are workspace-relative"],"tags":["mcp","path-validation","security"],"backgroundTag":"path-traversal-blocked","analyzedSha":"f465ad909315a22120636baf03fa5e28701a50cb","analyzedAt":"2026-08-29T01:27:42.024Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}